Changeset 762 for trunk/pg.py


Ignore:
Timestamp:
Jan 17, 2016, 11:32:18 AM (4 years ago)
Author:
cito
Message:

Docs and 100% test coverage for NotificationHandler?

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/pg.py

    r748 r762  
    106106    """A PostgreSQL client-side asynchronous notification handler."""
    107107
    108     def __init__(self, db, event, callback, arg_dict=None, timeout=None):
     108    def __init__(self, db, event, callback=None,
     109            arg_dict=None, timeout=None, stop_event=None):
    109110        """Initialize the notification handler.
    110111
    111         db       - PostgreSQL connection object.
    112         event    - Event (notification channel) to LISTEN for.
    113         callback - Event callback function.
    114         arg_dict - A dictionary passed as the argument to the callback.
    115         timeout  - Timeout in seconds; a floating point number denotes
    116                    fractions of seconds. If it is absent or None, the
    117                    callers will never time out.
    118         """
    119         if isinstance(db, DB):
    120             db = db.db
     112        You must pass a PyGreSQL database connection, the name of an
     113        event (notification channel) to listen for and a callback function.
     114
     115        You can also specify a dictionary arg_dict that will be passed as
     116        the single argument to the callback function, and a timeout value
     117        in seconds (a floating point number denotes fractions of seconds).
     118        If it is absent or None, the callers will never time out.  If the
     119        timeout is reached, the callback function will be called with a
     120        single argument that is None.  If you set the timeout to zero,
     121        the handler will poll notifications synchronously and return.
     122
     123        You can specify the name of the event that will be used to signal
     124        the handler to stop listening as stop_event. By default, it will
     125        be the event name prefixed with 'stop_'.
     126        """
    121127        self.db = db
    122128        self.event = event
    123         self.stop_event = 'stop_%s' % event
     129        self.stop_event = stop_event or 'stop_%s' % event
    124130        self.listening = False
    125131        self.callback = callback
     
    130136
    131137    def __del__(self):
    132         self.close()
     138        self.unlisten()
    133139
    134140    def close(self):
     
    156162        """Generate a notification.
    157163
    158         Note: If the main loop is running in another thread, you must pass
    159         a different database connection to avoid a collision.
    160         """
    161         if not db:
    162             db = self.db
     164        Optionally, you can pass a payload with the notification.
     165
     166        If you set the stop flag, a stop notification will be sent that
     167        will cause the handler to stop listening.
     168
     169        Note: If the notification handler is running in another thread, you
     170        must pass a different database connection since PyGreSQL database
     171        connections are not thread-safe.
     172        """
    163173        if self.listening:
     174            if not db:
     175                db = self.db
    164176            q = 'notify "%s"' % (self.stop_event if stop else self.event)
    165177            if payload:
     
    167179            return db.query(q)
    168180
    169     def __call__(self, close=False):
     181    def __call__(self):
    170182        """Invoke the notification handler.
    171183
    172         The handler is a loop that actually LISTENs for two NOTIFY messages:
    173 
    174         <event> and stop_<event>.
    175 
    176         When either of these NOTIFY messages are received, its associated
    177         'pid' and 'event' are inserted into <arg_dict>, and the callback is
    178         invoked with <arg_dict>. If the NOTIFY message is stop_<event>, the
    179         handler UNLISTENs both <event> and stop_<event> and exits.
     184        The handler is a loop that listens for notifications on the event
     185        and stop event channels.  When either of these notifications are
     186        received, its associated 'pid', 'event' and 'extra' (the payload
     187        passed with the notification) are inserted into its arg_dict
     188        dictionary and the callback is invoked with this dictionary as
     189        a single argument.  When the handler receives a stop event, it
     190        stops listening to both events and return.
     191
     192        In the special case that the timeout of the handler has been set
     193        to zero, the handler will poll all events synchronously and return.
     194        If will keep listening until it receives a stop event.
    180195
    181196        Note: If you run this loop in another thread, don't use the same
     
    183198        """
    184199        self.listen()
    185         _ilist = [self.db.fileno()]
    186 
     200        poll = self.timeout == 0
     201        if not poll:
     202            rlist = [self.db.fileno()]
    187203        while self.listening:
    188             ilist, _olist, _elist = select.select(_ilist, [], [], self.timeout)
    189             if ilist:
     204            if poll or select.select(rlist, [], [], self.timeout)[0]:
    190205                while self.listening:
    191206                    notice = self.db.getnotify()
     
    200215                    if event == self.stop_event:
    201216                        self.unlisten()
    202                     self.arg_dict['pid'] = pid
    203                     self.arg_dict['event'] = event
    204                     self.arg_dict['extra'] = extra
     217                    self.arg_dict.update(pid=pid, event=event, extra=extra)
    205218                    self.callback(self.arg_dict)
     219                if poll:
     220                    break
    206221            else:   # we timed out
    207222                self.unlisten()
     
    11441159        return self.query(q)
    11451160
    1146     def notification_handler(self, event, callback, arg_dict={}, timeout=None):
     1161    def notification_handler(self,
     1162            event, callback, arg_dict=None, timeout=None, stop_event=None):
    11471163        """Get notification handler that will run the given callback."""
    1148         return NotificationHandler(self.db, event, callback, arg_dict, timeout)
     1164        return NotificationHandler(self,
     1165            event, callback, arg_dict, timeout, stop_event)
    11491166
    11501167
Note: See TracChangeset for help on using the changeset viewer.