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

Changeset 1075

Show
Ignore:
Timestamp:
04/26/06 02:28:59
Author:
fumanchu
Message:

More tool improvements:

  1. Replaced request.execute_main with request.dispatch. Setting request.dispatch to None means no dispatch call.
  2. Better xmlrpc tool as a result.
  3. Tools now know their own names.
  4. Tools now mix config into args given to wrap() and handler().
Files:

Legend:

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

    r1074 r1075  
    2727         
    2828        self.scheme = scheme 
    29         self.execute_main = True 
    3029        self.closed = False 
    3130         
     
    8887            # right away. 
    8988            self.processRequestLine() 
     89            self.dispatch = cherrypy.config.get("dispatch") or dispatch 
    9090            self.hooks.setup() 
    9191             
     
    104104                        try: 
    105105                            self.hooks.run('before_main') 
    106                             if self.execute_main
    107                                 self.main(
     106                            if self.dispatch
     107                                self.dispatch(self.object_path
    108108                            break 
    109109                        except cherrypy.InternalRedirect, ir: 
     
    168168        server_v = httptools.Version.from_http(server_v) 
    169169        cherrypy.response.version = min(self.version, server_v) 
    170      
    171     def main(self, path=None): 
    172         """Obtain and set cherrypy.response.body from a page handler.""" 
    173         if path is None: 
    174             path = self.object_path 
    175         _dispatch = cherrypy.config.get("dispatcher") or dispatch 
    176         _dispatch(path) 
    177170     
    178171    def processHeaders(self): 
  • trunk/cherrypy/lib/caching.py

    r1057 r1075  
    142142    """Caching decorator.""" 
    143143    def wrapper(*a, **kw): 
     144        # There's are no parameters to get(), so there's no need 
     145        # to merge values from config. 
    144146        if not get(): 
    145147            f(*a, **kw) 
     
    153155    def wrapper(): 
    154156        if get(): 
    155             cherrypy.request.execute_main = Fals
     157            cherrypy.request.dispatch = Non
    156158        else: 
    157159            # Note the devious technique here of adding hooks on the fly 
  • trunk/cherrypy/lib/sessions.py

    r1073 r1075  
    414414        sess.storage = None 
    415415 
    416 def wrap(*args, **kwargs): 
    417     """Make a decorator for this tool.""" 
    418     def deco(f): 
    419         def wrapper(*a, **kw): 
    420             result = f(*a, **kw) 
    421             save(*args, **kwargs) 
    422             cherrypy.request.hooks.attach('on_end_request', cleanup) 
    423             return result 
    424         return wrapper 
    425     return deco 
    426  
    427 def setup(conf): 
    428     """Hook this tool into cherrypy.request using the given conf. 
    429      
    430     The standard CherryPy request object will automatically call this 
    431     method when the tool is "turned on" in config. 
    432     """ 
    433     def wrapper(): 
    434         s = cherrypy.request._session = Session() 
    435         for k, v in conf.iteritems(): 
    436             setattr(s, str(k), v) 
    437         s.init() 
    438          
    439         if not hasattr(cherrypy, "session"): 
    440             cherrypy.session = SessionWrapper() 
    441      
    442     cherrypy.request.hooks.attach('before_request_body', wrapper) 
    443     cherrypy.request.hooks.attach('before_finalize', save) 
    444     cherrypy.request.hooks.attach('on_end_request', cleanup) 
  • trunk/cherrypy/lib/static.py

    r1069 r1075  
    166166        return False 
    167167 
    168 def get_dir(section, dir, root="", match="", content_types=None, index=""): 
     168def staticdir(section, dir, root="", match="", content_types=None, index=""): 
    169169    if match and not re.search(match, cherrypy.request.object_path): 
    170170        return False 
     
    201201    return handled 
    202202 
    203 def get_file(filename, root=None, match="", content_types=None): 
     203def staticfile(filename, root=None, match="", content_types=None): 
    204204    if match and not re.search(match, cherrypy.request.object_path): 
    205205        return False 
  • trunk/cherrypy/lib/xmlrpc.py

    r1055 r1075  
    66 
    77def process_body(): 
    8     request = cherrypy.request 
    9      
    10     cl = int(request.headers.get('Content-Length') or 0) 
    11     data = request.rfile.read(cl) 
     8    """Return (params, method) from request body.""" 
    129    try: 
    13         params, method = xmlrpclib.loads(data
     10        return xmlrpclib.loads(cherrypy.request.body.read()
    1411    except Exception: 
    15         params, method = ('ERROR PARAMS', ), 'ERRORMETHOD' 
    16     request.rpcMethod, request.rpcParams = method, params 
    17      
    18     # patch the path. there are only a few options: 
    19     # - 'RPC2' + method >> method 
    20     # - 'someurl' + method >> someurl.method 
    21     # - 'someurl/someother' + method >> someurl.someother.method 
    22     if not request.object_path.endswith('/'): 
    23         request.object_path += '/' 
    24     if request.object_path.startswith('/RPC2/'): 
     12        return ('ERROR PARAMS', ), 'ERRORMETHOD' 
     13 
     14 
     15def patched_path(path, method): 
     16    """Return 'path' with the rpcMethod appended.""" 
     17    if not path.endswith('/'): 
     18        path += '/' 
     19    if path.startswith('/RPC2/'): 
    2520        # strip the first /rpc2 
    26         request.object_path = request.object_path[5:] 
    27     request.object_path += str(method).replace('.', '/') 
    28     request.paramList = list(params) 
    29     request.processRequestBody = False 
     21        path = path[5:] 
     22    path += str(method).replace('.', '/') 
     23    return path 
    3024 
    31 def main(encoding='utf-8', allow_none=0): 
    32     """Obtain and set cherrypy.response.body from a page handler. 
    33      
    34     Python's None value cannot be used in standard XML-RPC; to allow 
    35     using it via an extension, provide a true value for allow_none. 
    36     """ 
    37     from cherrypy import _cprequest 
    38     dispatch = cherrypy.config.get("dispatcher") or _cprequest.Dispatcher() 
    39      
    40     request = cherrypy.request 
    41     handler = dispatch(request.object_path) 
    42     body = handler(*(request.virtual_path + request.paramList), 
    43                    **request.params) 
    44     respond(xmlrpclib.dumps((body,), methodresponse=1, 
    45                             encoding=encoding, allow_none=allow_none)) 
    46     return True 
    4725 
    48 def error_response(): 
    49     body = str(sys.exc_info()[1]) 
    50     respond(xmlrpclib.dumps(xmlrpclib.Fault(1, body))) 
    51  
    52 def respond(body): 
     26def _set_response(body): 
    5327    # The XML-RPC spec (http://www.xmlrpc.com/spec) says: 
    5428    # "Unless there's a lower-level error, always return 200 OK." 
     
    6135    response.headers['Content-Length'] = len(body) 
    6236 
    63 def setup(conf): 
    64     """Hook this tool into cherrypy.request using the given conf.""" 
    65     cherrypy.request.hooks.attach('before_request_body', process_body, conf) 
    66     def wrapper(): 
    67         if main(**conf): 
    68             cherrypy.request.execute_main = False 
    69     cherrypy.request.hooks.attach('before_main', wrapper) 
    70     cherrypy.request.hooks.attach('after_error_response', error_response, conf) 
     37 
     38def respond(body, encoding='utf-8', allow_none=0): 
     39    _set_response(xmlrpclib.dumps((body,), methodresponse=1, 
     40                                  encoding=encoding, 
     41                                  allow_none=allow_none)) 
     42 
     43def wrap_error(): 
     44    body = str(sys.exc_info()[1]) 
     45    _set_response(xmlrpclib.dumps(xmlrpclib.Fault(1, body))) 
     46 
  • trunk/cherrypy/test/test_core.py

    r1074 r1075  
    495495            data = open(log_file, "rb").readlines() 
    496496            self.assertEqual(data[-3], '    raise ValueError()\n') 
    497             # Each error should write only one traceback (11 lines each). 
    498             self.assertEqual(len(data), 22
     497            # Each error should write only one traceback (9 lines each). 
     498            self.assertEqual(len(data), 18
    499499        finally: 
    500500            ignore.pop() 
  • trunk/cherrypy/tools.py

    r1069 r1075  
    1616    CherryPy hooks: "hooks" are points in the CherryPy request-handling 
    1717        process which may hand off control to registered callbacks. The 
    18         Request object possesses a "hooks" attribute for manipulating 
    19         this. If a tool exposes a "setup" callable, this will be called 
    20         once per Request (if the feature is enabled via config). 
     18        Request object possesses a "hooks" attribute (a tools.HookMap) 
     19        for manipulating this. If a tool exposes a "setup" callable, 
     20        it will be called once per Request (if the feature is enabled 
     21        via config). 
    2122 
    2223Tools may be implemented as any object with a namespace. The builtins 
     
    4344            self.callbacks[point].append(wrapper) 
    4445     
    45     def tool_config(self): 
    46         toolmap = {} 
    47         for k, v in cherrypy.config.current_config().iteritems(): 
    48             atoms = k.split(".") 
    49             namespace = atoms.pop(0) 
    50             if namespace == "tools": 
    51                 toolname = atoms.pop(0) 
    52                 bucket = toolmap.setdefault(toolname, {}) 
    53                 bucket[".".join(atoms)] = v 
    54         return toolmap 
    55      
    5646    def setup(self): 
    5747        """Run tool.setup(conf) for each tool specified in current config.""" 
    5848        g = globals() 
    59         for toolname, conf in self.tool_config().iteritems(): 
     49        for toolname, conf in tool_config().iteritems(): 
    6050            if conf.get("on", False): 
    6151                del conf["on"] 
    6252                g[toolname].setup(conf) 
    6353         
    64         # Run _cp_setup_hooks functions 
     54        # Run _cp_setup functions 
    6555        mounted_app_roots = cherrypy.tree.mount_points.values() 
    6656        objectList = _cputil.get_object_trail() 
    6757        objectList.reverse() 
    6858        for objname, obj in objectList: 
    69             s = getattr(obj, "_cp_setup_hooks", None) 
     59            s = getattr(obj, "_cp_setup", None) 
    7060            if s: 
    7161                s() 
     
    9383                callback() 
    9484 
     85def tool_config(): 
     86    """Return all 'tools.*' config entries as a {toolname: {k: v}} dict.""" 
     87    toolmap = {} 
     88    for k, v in cherrypy.config.current_config().iteritems(): 
     89        atoms = k.split(".") 
     90        namespace = atoms.pop(0) 
     91        if namespace == "tools": 
     92            toolname = atoms.pop(0) 
     93            bucket = toolmap.setdefault(toolname, {}) 
     94            bucket[".".join(atoms)] = v 
     95    return toolmap 
     96 
     97def merged_config(toolname, d): 
     98    """Merge arguments from tool config into another dict.""" 
     99    mergedkw = d.copy() 
     100    mergedkw.update(tool_config().get(toolname, {})) 
     101    if "on" in mergedkw: 
     102        del mergedkw["on"] 
     103    return mergedkw 
     104 
    95105 
    96106class Tool(object): 
    97107     
    98     def __init__(self, point, callable): 
     108    def __init__(self, point, callable, name=None): 
    99109        self.point = point 
    100110        self.callable = callable 
     111        if name is None: 
     112            name = callable.__name__ 
     113        self.name = name 
    101114        # TODO: add an attribute to self for each arg 
    102115        # in inspect.getargspec(callable) 
     116##         
     117##        class ToolMixin(object): 
     118##            def _cp_setup(me): 
     119##                self.setup(None) 
     120##        self.Mixin = ToolMixin 
    103121     
    104122    def __call__(self, *args, **kwargs): 
     
    117135        def deco(f): 
    118136            def wrapper(*a, **kw): 
    119                 handled = self.callable(*args, **kwargs
     137                handled = self.callable(*args, **merged_config(self.name, kwargs)
    120138                return f(*a, **kw) 
    121139            return wrapper 
     
    138156    """ 
    139157     
    140     def __init__(self, callable): 
     158    def __init__(self, callable, name=None): 
    141159        self.point = 'before_main' 
    142160        self.callable = callable 
     161        if name is None: 
     162            name = callable.__name__ 
     163        self.name = name 
    143164     
    144165    def handler(self, *args, **kwargs): 
     
    150171        """ 
    151172        def wrapper(*a, **kw): 
    152             handled = self.callable(*args, **kwargs
     173            handled = self.callable(*args, **merged_config(self.name, kwargs)
    153174            if not handled: 
    154175                raise cherrypy.NotFound() 
     
    169190        def deco(f): 
    170191            def wrapper(*a, **kw): 
    171                 handled = self.callable(*args, **kwargs
     192                handled = self.callable(*args, **merged_config(self.name, kwargs)
    172193                if handled: 
    173194                    return cherrypy.response.body 
     
    185206        def wrapper(): 
    186207            if self.callable(**conf): 
    187                 cherrypy.request.execute_main = Fals
     208                cherrypy.request.dispatch = Non
    188209        # Don't pass conf (or our wrapper will get wrapped!) 
    189210        cherrypy.request.hooks.attach(self.point, wrapper) 
     
    209230    def setup(self, conf): 
    210231        """Hook this tool into cherrypy.request using the given conf.""" 
     232        # Stick the section where "dir" was defined into the params 
    211233        conf['section'] = cherrypy.config.get('tools.staticdir.dir', 
    212234                                              return_section=True) 
    213235        def wrapper(): 
    214236            if self.callable(**conf): 
    215                 cherrypy.request.execute_main = Fals
     237                cherrypy.request.dispatch = Non
    216238        # Don't pass conf (or our wrapper will get wrapped!) 
    217239        cherrypy.request.hooks.attach(self.point, wrapper) 
    218 staticdir = _StaticDirTool(static.get_dir) 
    219 staticfile = MainTool(static.get_file) 
     240staticdir = _StaticDirTool(static.staticdir) 
     241staticfile = MainTool(static.staticfile) 
    220242del static 
    221243 
     244from cherrypy.lib import sessions as _sessions 
     245class _SessionTool(Tool): 
     246    def __init__(self): 
     247        self.point = "before_finalize" 
     248        self.callable = _sessions.save 
     249        self.name = "sessions" 
     250     
     251    def wrap(self, **kwargs): 
     252        """Make a decorator for this tool.""" 
     253        def deco(f): 
     254            def wrapper(*a, **kw): 
     255                s = cherrypy.request._session = _sessions.Session() 
     256                for k, v in merged_config(self.name, kwargs).iteritems(): 
     257                    setattr(s, str(k), v) 
     258                s.init() 
     259                if not hasattr(cherrypy, "session"): 
     260                    cherrypy.session = _sessions.SessionWrapper() 
     261                 
     262                result = f(*a, **kw) 
     263                _sessions.save() 
     264                cherrypy.request.hooks.attach('on_end_request', _sessions.cleanup) 
     265                return result 
     266            return wrapper 
     267        return deco 
     268     
     269    def setup(self, conf): 
     270        """Hook this tool into cherrypy.request using the given conf. 
     271         
     272        The standard CherryPy request object will automatically call this 
     273        method when the tool is "turned on" in config. 
     274        """ 
     275        def init(): 
     276            s = cherrypy.request._session = _sessions.Session() 
     277            for k, v in conf.iteritems(): 
     278                setattr(s, str(k), v) 
     279            s.init() 
     280             
     281            if not hasattr(cherrypy, "session"): 
     282                cherrypy.session = _sessions.SessionWrapper() 
     283        cherrypy.request.hooks.attach('before_request_body', init) 
     284         
     285        cherrypy.request.hooks.attach('before_finalize', _sessions.save) 
     286        cherrypy.request.hooks.attach('on_end_request', _sessions.cleanup) 
     287sessions = _SessionTool() 
     288 
     289from cherrypy.lib import xmlrpc as _xmlrpc 
     290class _XMLRPCTool(object): 
     291    """Tool for using XMLRPC over HTTP. 
     292     
     293    Python's None value cannot be used in standard XML-RPC; to allow 
     294    using it via an extension, provide a true value for allow_none. 
     295    """ 
     296     
     297    def dispatch(self, path): 
     298        """Use this tool for cherrypy.request dispatch. 
     299         
     300        For example: 
     301            [/rpc] 
     302            dispatch = tools.xmlrpc.dispatch 
     303        """ 
     304        request.hooks.attach('after_error_response', _xmlrpc.wrap_error) 
     305         
     306        rpcparams, rpcmethod = _xmlrpc.process_body() 
     307        path = _xmlrpc.patched_path(path, rpcmethod) 
     308         
     309        from cherrypy import _cprequest 
     310        handler, opath, vpath = _cprequest.find(path) 
     311         
     312        # Decode any leftover %2F in the virtual_path atoms. 
     313        vpath = tuple([x.replace("%2F", "/") for x in vpath]) 
     314         
     315        request = cherrypy.request 
     316        body = handler(*(vpath + rpcparams), **request.params) 
     317        conf = tool_config().get("xmlrpc", {}) 
     318        _xmlrpc.respond(body, 
     319                        conf.get('encoding', 'utf-8'), 
     320                        conf.get('allow_none', 0)) 
     321     
     322    def setup(self, conf): 
     323        """Hook this tool into cherrypy.request using the given conf.""" 
     324        cherrypy.request.dispatch = self.dispatch 
     325xmlrpc = _XMLRPCTool() 
     326 
     327 
    222328# These modules are themselves Tools 
    223 from cherrypy.lib import caching, sessions, xmlrpc 
     329from cherrypy.lib import caching 

Hosted by WebFaction

Log in as guest/cpguest to create tickets