Changeset 511 for trunk/module/pg.py


Ignore:
Timestamp:
Jan 7, 2013, 4:10:55 PM (7 years ago)
Author:
cito
Message:

Simplify and rename notification handler again.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/module/pg.py

    r508 r511  
    3333import select
    3434import warnings
    35 from threading import Lock
    3635try:
    3736    frozenset
     
    142141
    143142
    144 class WhenNotified(object):
     143class NotificationHandler(object):
    145144    """A PostgreSQL client-side asynchronous notification handler."""
    146145
     
    148147        """Initialize the notification handler.
    149148
    150         db   - PostgreSQL connection object.
    151         event    - Event to LISTEN for.
    152         callback - Event callback.
     149        db       - PostgreSQL connection object.
     150        event    - Event (notification channel) to LISTEN for.
     151        callback - Event callback function.
    153152        arg_dict - A dictionary passed as the argument to the callback.
    154153        timeout  - Timeout in seconds; a floating point number denotes
    155                     fractions of seconds. If it is absent or None, the
    156                     callers will never time out."""
    157 
     154                   fractions of seconds. If it is absent or None, the
     155                   callers will never time out.
     156
     157        """
    158158        if isinstance(db, DB):
    159159            db = db.db
    160160        self.db = db
    161161        self.event = event
    162         self.stop = 'stop_%s' % event
     162        self.stop_event = 'stop_%s' % event
    163163        self.listening = False
    164164        self.callback = callback
     
    167167        self.arg_dict = arg_dict
    168168        self.timeout = timeout
    169         self.lock = Lock()
    170169
    171170    def __del__(self):
     
    173172
    174173    def close(self):
     174        """Stop listening and close the connection."""
    175175        if self.db:
    176176            self.unlisten()
     
    179179
    180180    def listen(self):
     181        """Start listening for the event and the stop event."""
    181182        if not self.listening:
    182             self.lock.acquire()
    183             try:
    184                 self.db.query('listen "%s"' % self.event)
    185                 self.db.query('listen "%s"' % self.stop)
    186                 self.listening = True
    187             finally:
    188                 self.lock.release()
     183            self.db.query('listen "%s"' % self.event)
     184            self.db.query('listen "%s"' % self.stop_event)
     185            self.listening = True
    189186
    190187    def unlisten(self):
     188        """Stop listening for the event and the stop event."""
    191189        if self.listening:
    192             self.lock.acquire()
    193             try:
    194                 self.db.query('unlisten "%s"' % self.event)
    195                 self.db.query('unlisten "%s"' % self.stop)
    196                 self.listening = False
    197             finally:
    198                 self.lock.release()
    199 
    200     def notify(self, stop=False, payload=None):
     190            self.db.query('unlisten "%s"' % self.event)
     191            self.db.query('unlisten "%s"' % self.stop_event)
     192            self.listening = False
     193
     194    def notify(self, db=None, stop=False, payload=None):
     195        """Generate a notification.
     196
     197        Note: If the main loop is running in another thread, you must pass
     198        a different database connection to avoid a collision.
     199
     200        """
     201        if not db:
     202            db = self.db
    201203        if self.listening:
    202             q = 'notify "%s"' % (stop and self.stop or self.event)
     204            q = 'notify "%s"' % (stop and self.stop_event or self.event)
    203205            if payload:
    204206                q += ", '%s'" % payload
    205             self.lock.acquire()
    206             try:
    207                 ret = self.db.query(q)
    208             finally:
    209                 self.lock.release()
    210             return ret
    211 
    212     def __call__(self):
    213         """Invoke the handler.
     207            return db.query(q)
     208
     209    def __call__(self, close=False):
     210        """Invoke the notification handler.
    214211
    215212        The handler is a loop that actually LISTENs for two NOTIFY messages:
     
    221218        invoked with <arg_dict>. If the NOTIFY message is stop_<event>, the
    222219        handler UNLISTENs both <event> and stop_<event> and exits.
     220
     221        Note: If you run this loop in another thread, don't use the same
     222        database connection for database operations in the main thread.
    223223
    224224        """
     
    233233                break
    234234            else:
    235                 self.lock.acquire()
    236                 try:
    237                     notice = self.db.getnotify()
    238                 finally:
    239                     self.lock.release()
     235                notice = self.db.getnotify()
    240236                if notice is None:
    241237                    continue
    242238                event, pid, extra = notice
    243                 if event in (self.event, self.stop):
     239                if event in (self.event, self.stop_event):
    244240                    self.arg_dict['pid'] = pid
    245241                    self.arg_dict['event'] = event
    246242                    self.arg_dict['extra'] = extra
    247243                    self.callback(self.arg_dict)
    248                     if event == self.stop:
     244                    if event == self.stop_event:
    249245                        self.unlisten()
    250246                        break
     
    253249                    raise _db_error(
    254250                        'listening for "%s" and "%s", but notified of "%s"'
    255                         % (self.event, self.stop, event))
     251                        % (self.event, self.stop_event, event))
    256252
    257253
    258254def pgnotify(*args, **kw):
    259     """Same as WhenNotified, under the traditional name."""
    260     warnings.warn("pgnotify is deprecated, use WhenNotified instead.",
     255    """Same as NotificationHandler, under the traditional name."""
     256    warnings.warn("pgnotify is deprecated, use NotificationHandler instead.",
    261257        DeprecationWarning, stacklevel=2)
    262     return WhenNotified(*args, **kw)
     258    return NotificationHandler(*args, **kw)
    263259
    264260
     
    964960        return int(self.db.query(q))
    965961
    966     def when_notified(self, event, callback, arg_dict={}, timeout=None):
     962    def notification_handler(self, event, callback, arg_dict={}, timeout=None):
    967963        """Get notification handler that will run the given callback."""
    968         return WhenNotified(self.db, event, callback, arg_dict, timeout)
     964        return NotificationHandler(self.db, event, callback, arg_dict, timeout)
    969965
    970966
Note: See TracChangeset for help on using the changeset viewer.