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

Changeset 1280

Show
Ignore:
Timestamp:
08/26/06 01:26:58
Author:
fumanchu
Message:

Hook priority system:

  1. All hook callbacks are now wrapped in an instance of _cprequest.Hook, where failsafe, priority, and kwargs are now stored.
  2. Hooks are run in order of their "priority" attribute; lower numbers first. Default 50, range 0-100, floats OK.
  3. cherrypy namespace: moved "serving" to "_serving", added cherrypy.Tool, changed "LogManager" to "_LogManager".
  4. Set gzip.priority to 90.
Files:

Legend:

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

    r1278 r1280  
    99from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect, NotFound 
    1010from cherrypy._cperror import WrongConfigValue, TimeoutError 
    11 error_page = {} 
    1211 
    1312from cherrypy import _cptools 
    1413tools = _cptools.default_toolbox 
     14Tool = _cptools.Tool 
    1515 
    1616from cherrypy import _cptree 
     
    3737# a new HTTP conversation, yet still refer to them as module-level globals 
    3838# in a thread-safe way. 
    39 serving = _local() 
     39_serving = _local() 
    4040 
    4141 
     
    5050    def _get_child(self): 
    5151        try: 
    52             return getattr(serving, self.__attrname__) 
     52            return getattr(_serving, self.__attrname__) 
    5353        except AttributeError: 
    5454            # Bind dummy instances of default objects to help introspection. 
     
    8383# Create request and response object (the same objects will be used 
    8484#   throughout the entire life of the webserver, but will redirect 
    85 #   to the "serving" object) 
     85#   to the "_serving" object) 
    8686from cherrypy.lib import http as _http 
    8787request = _ThreadLocalProxy('request', 
     
    113113 
    114114 
    115 class LogManager(object): 
     115class _LogManager(object): 
    116116     
    117117    screen = True 
     
    158158 
    159159 
    160 log = LogManager() 
     160log = _LogManager() 
    161161 
    162162 
  • trunk/cherrypy/_cpengine.py

    r1278 r1280  
    210210            req = self.request_class(local_host, remote_host, scheme, 
    211211                                     server_protocol) 
    212         cherrypy.serving.request = req 
    213         cherrypy.serving.response = resp = self.response_class() 
     212        cherrypy._serving.request = req 
     213        cherrypy._serving.response = resp = self.response_class() 
    214214        self.servings.append((req, resp)) 
    215215        return req 
  • trunk/cherrypy/_cprequest.py

    r1278 r1280  
    1313 
    1414 
    15 class HookMap(object): 
    16     """A map of call points to callback lists. 
    17      
    18     callbacks: a dict of the form {call point: [callbacks]}. 
    19         Each 'call point' is a name and each callback is a callable 
    20         that takes no arguments. 
    21     failsafes: a dict of the form {callback: failsafe}. If 'failsafe' is 
    22         True, the callback is guaranteed to run even if other callbacks 
    23         from the same call point raise exceptions. False values are 
    24         permissible, but ignored. 
     15class Hook(object): 
     16    """A callback and its metadata: failsafe, priority, and kwargs. 
     17     
     18    failsafe: If True, the callback is guaranteed to run even if other 
     19        callbacks from the same call point raise exceptions. 
     20    priority: Defines the order of execution for a list of Hooks. 
     21        Defaults to 50. Priority numbers should be limited to the 
     22        closed interval [0, 100], but values outside this range are 
     23        acceptable, as are fractional values. 
    2524    """ 
    2625     
    27     def __init__(self, points=None): 
    28         points = points or [] 
    29         self.callbacks = dict([(point, []) for point in points]) 
    30         self.failsafes = {} 
    31      
    32     def attach(self, point, callback, failsafe=None, **kwargs): 
    33         """Append callback at the given call point. 
    34          
    35         If failsafe is True, the supplied callback is guaranteed to 
    36             run, even is other callbacks at the same call point fail. 
    37             If failsafe is None or not given, callback.failsafe will 
    38             be used if present; otherwise, False is assumed. 
    39         If additional keyword args are provided, they will be passed 
    40             to the given callback for each call. 
    41         """ 
    42         func = callback 
    43         if kwargs: 
    44             def wrapper(): 
    45                 callback(**kwargs) 
    46             func = wrapper 
    47             name = getattr(callback, "__name__", None) 
    48             if name: 
    49                 func.__name__ = name 
    50         self.callbacks[point].append(func) 
     26    def __init__(self, callback, failsafe=None, priority=None, **kwargs): 
     27        self.callback = callback 
     28         
    5129        if failsafe is None: 
    52             failsafe = getattr(callback, 'failsafe', False) 
    53         self.failsafes[func] = failsafe 
     30            failsafe = getattr(callback, "failsafe", False) 
     31        self.failsafe = failsafe 
     32         
     33        if priority is None: 
     34            priority = getattr(callback, "priority", 50) 
     35        self.priority = priority 
     36         
     37        self.kwargs = kwargs 
     38     
     39    def __cmp__(self, other): 
     40        return cmp(self.priority, other.priority) 
     41     
     42    def __call__(self): 
     43        return self.callback(**self.kwargs) 
     44 
     45 
     46class HookMap(dict): 
     47    """A map of call points to lists of callbacks (Hook objects).""" 
     48     
     49    def __new__(cls, points=None): 
     50        d = dict.__new__(cls) 
     51        for p in points or []: 
     52            d[p] = [] 
     53        return d 
     54     
     55    def __init__(self, *a, **kw): 
     56        pass 
     57     
     58    def attach(self, point, callback, failsafe=None, priority=None, **kwargs): 
     59        """Append a new Hook made from the supplied arguments.""" 
     60        self[point].append(Hook(callback, failsafe, priority, **kwargs)) 
    5461     
    5562    def run(self, point): 
    56         """Execute all registered callbacks for the given point.""" 
     63        """Execute all registered Hooks (callbacks) for the given point.""" 
    5764        if cherrypy.response.timed_out: 
    5865            raise cherrypy.TimeoutError() 
    5966         
    6067        exc = None 
    61         for callback in self.callbacks[point]: 
    62             # Some hookpoints guarantee all callbacks are run even if 
    63             # others at the same hookpoint fail. We will still log the 
    64             # failure, but proceed on to the next callback. The only way 
    65             # to stop all processing from one of these callbacks is 
    66             # to raise SystemExit and stop the whole server. So, trap 
    67             # your own errors in these callbacks! 
    68             if exc is None or self.failsafes.get(callback, False): 
     68        hooks = self[point] 
     69        hooks.sort() 
     70        for hook in hooks: 
     71            # Some hooks are guaranteed to run even if others at 
     72            # the same hookpoint fail. We will still log the failure, 
     73            # but proceed on to the next hook. The only way 
     74            # to stop all processing from one of these hooks is 
     75            # to raise SystemExit and stop the whole server. 
     76            if exc is None or hook.failsafe: 
    6977                try: 
    70                     callback() 
     78                    hook() 
    7179                except (KeyboardInterrupt, SystemExit): 
    7280                    raise 
     
    320328        self.closed = False 
    321329        self.redirections = [] 
     330         
     331        # Put a *copy* of the class error_page into self. 
     332        self.error_page = self.error_page.copy() 
    322333     
    323334    def close(self): 
     
    326337            self.hooks.run('on_end_request') 
    327338             
    328             s = (self, cherrypy.serving.response) 
     339            s = (self, cherrypy._serving.response) 
    329340            try: 
    330341                cherrypy.engine.servings.remove(s) 
     
    332343                pass 
    333344             
    334             cherrypy.serving.__dict__.clear() 
     345            cherrypy._serving.__dict__.clear() 
    335346     
    336347    def run(self, method, path, query_string, req_protocol, headers, rfile): 
     
    532543    def tool_up(self): 
    533544        """Process self.config, populate self.toolmap and set up each tool.""" 
    534         self.error_page = cherrypy.error_page.copy() 
    535          
    536545        # Get all 'tools.*' config entries as a {toolname: {k: v}} dict. 
    537546        self.toolmap = tm = {} 
     
    546555            elif namespace == "hooks": 
    547556                # Attach bare hooks declared in config. 
    548                 hookpoint = atoms[1] 
     557                # Use split again to allow multiple hooks for a single 
     558                # hookpoint per path (e.g. "hooks.before_main.1"). 
     559                # Little-known fact you only get from reading source ;) 
     560                hookpoint = atoms[1].split(".", 1)[0] 
    549561                v = reqconf[k] 
    550562                if isinstance(v, basestring): 
    551563                    v = cherrypy.lib.attributes(v) 
    552                 self.hooks.attach(hookpoint, v) 
     564                if not isinstance(v, Hook): 
     565                    v = Hook(v) 
     566                self.hooks[hookpoint].append(v) 
    553567            elif namespace == "request": 
    554568                # Override properties of this request object. 
  • trunk/cherrypy/_cptools.py

    r1275 r1280  
    122122        return wrapper 
    123123     
    124     def _wrapper(self): 
    125         if self.callable(**self._merged_args()): 
     124    def _wrapper(self, **kwargs): 
     125        if self.callable(**kwargs): 
    126126            cherrypy.request.handler = None 
    127127     
     
    132132        method when the tool is "turned on" in config. 
    133133        """ 
    134         # Don't pass conf (or our wrapper will get wrapped!) 
    135134        f = getattr(self.callable, "failsafe", False) 
    136         cherrypy.request.hooks.attach(self._point, self._wrapper, failsafe=f) 
     135        p = getattr(self.callable, "priority", 50) 
     136        cherrypy.request.hooks.attach(self._point, self._wrapper, failsafe=f, 
     137                                      priority=p, **self._merged_args()) 
    137138 
    138139 
  • trunk/cherrypy/lib/encoding.py

    r1275 r1280  
    217217            return 
    218218    cherrypy.HTTPError(406, "identity, gzip").set_response() 
    219  
     219gzip.priority = 90 
  • trunk/cherrypy/lib/sessions.py

    r1278 r1280  
    381381    kwargs['timeout'] = timeout 
    382382    kwargs['clean_freq'] = clean_freq 
    383     cherrypy.serving.session = sess = globals()[storage_class](id, **kwargs) 
     383    cherrypy._serving.session = sess = globals()[storage_class](id, **kwargs) 
    384384     
    385385    if locking == 'implicit': 
  • trunk/cherrypy/test/test_tools.py

    r1278 r1280  
    77 
    88import cherrypy 
    9 from cherrypy import _cptools, tools 
     9from cherrypy import tools 
    1010 
    1111 
     
    1717        if not getattr(cherrypy.request, "login", None): 
    1818            raise cherrypy.HTTPError(401) 
    19     tools.check_access = _cptools.Tool('before_request_body', check_access) 
     19    tools.check_access = cherrypy.Tool('before_request_body', check_access) 
    2020     
    2121    def numerify(): 
     
    2727        cherrypy.response.body = number_it(cherrypy.response.body) 
    2828     
    29     class NumTool(_cptools.Tool): 
     29    class NumTool(cherrypy.Tool): 
    3030        def _setup(self): 
    3131            def makemap(): 
     
    3737                cherrypy.request.error_response = cherrypy.HTTPError(502).set_response 
    3838            critical.failsafe = True 
     39             
    3940            cherrypy.request.hooks.attach('on_start_resource', critical) 
    40              
    4141            cherrypy.request.hooks.attach(self._point, self.callable) 
    4242     
    4343    tools.numerify = NumTool('before_finalize', numerify) 
    4444     
    45     # It's not mandatory to inherit from _cptools.Tool. 
     45    # It's not mandatory to inherit from cherrypy.Tool. 
    4646    class NadsatTool: 
    4747         
     
    5858                    yield chunk 
    5959            cherrypy.response.body = nadsat_it_up(cherrypy.response.body) 
     60        nadsat.priority = 0 
    6061         
    6162        def cleanup(self): 
     
    6869            cherrypy.request.counter = self.counter = self.counter + 1 
    6970            self.ended[cherrypy.request.counter] = False 
    70             cherrypy.request.hooks.callbacks['before_finalize'].insert(0, self.nadsat) 
     71            cherrypy.request.hooks.attach('before_finalize', self.nadsat) 
    7172            cherrypy.request.hooks.attach('on_end_request', self.cleanup) 
    7273    tools.nadsat = NadsatTool() 
     
    9798        # XXX Note that encode must run before gzip. 
    9899        def decorated_euro(self): 
    99             print cherrypy.request.hooks.callbacks 
    100100            yield u"Hello," 
    101101            yield u"world" 

Hosted by WebFaction

Log in as guest/cpguest to create tickets