Return to Snippet

Revision: 6729
at June 16, 2008 10:32 by chombee


Updated Code
"""
messager.py -- a simple message-passing pattern for one-many or many-many 
               dependencies. Useful for event notifications, for example.

To send a message use the singleton messager instance:

    from messager import messager

    messager.send('message name',argument)

You can pass a single argument with a message, and this argument can be anything
you like. For example, event objects that simply hold a number of attributes can
be constrcuted and passed as arguments with messages.
    
Messager maintains a mapping of message names to lists of functions (and their
arguments). When a message is sent, all of the functions subscribed to that
message are called and passed the argument given when the function was
subscribed followed by argument given when the message was sent. To subscribe a
function you must subclass Receiver and call the accept(...)  or acceptOnce(...) 
methods:

    self.accept('message name',function,argument)

    self.acceptOnce('message name',function,argument)

You don't need to call Receiver.__init__() when you subclass Receiver, it has no
__init__. Receiver works by maintaining a list of the message subscriptions you
have made.

It is up to you to make sure that functions that accept messages take the right
number of arguments, 0, 1 or 2 depending on whether the accept(...) and
send(...) methods were called with an argument or not.

To unsubscribe a function from a message name use ignore:

    # Unsubscribe a particular function from a particular message name.
    self.ignore('message name',function)

    # Unsubscribe all functions that this object has subscribed to a particular 
    # message name.
    self.ignore('message name')

    # Unsubscribe all functions that this object has subscribed to any message
    # name.
    self.ignoreAll()

You can unsubscribe all functions from all Receiver objects with:

    messager.clear()
        
If you do `messager.verbose = True` the messager will print whenever it
receives a message or subscription, and if you do `print messager` the messager
will print out a list of all the registered message names and their subscribers.

One last thing to be aware of is that messager keeps references to (functions
of) all objects that subscribe to accept messages. For an object to be deleted
it must unsubscribe all of its functions from all messages (the ignoreAll()
method will do this).

"""

class Messager:
    """Singleton messager object."""
   
    def __init__(self):
        """Initialise the dictionary mapping message names to lists of receiver 
        functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    def send(self,name,sender_arg=None):
        """Send a message with the given name and the given argument. All
        functions registered as receivers of this message name will be
        called."""
                
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                args = []
                if receiver['arg'] is not None:
                    args.append(receiver['arg'])
                if sender_arg is not None:
                    args.append(sender_arg)
                receiver['function'](*args)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                args = []
                if receiver['arg'] is not None:
                    args.append(receiver['arg'])
                if sender_arg is not None:
                    args.append(sender_arg)
                receiver['function'](*args)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def _accept(self,name,function,arg=None):
        """Register with the messager to receive messages with the given name,
        messager will call the given function to notify of a message. The arg
        object given to accept will be passed to the given function first,
        followed by the arg object given to send by the sender object."""

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'arg':arg})

        if self.verbose:
            print '',function,'subscribed to event',name,'with arg',arg
        
    def _acceptOnce(self,name,function,arg=None):
        """Register to receive the next instance only of a message with the
        given name."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'arg':arg})

        if self.verbose:
            print '',function,'subscribed to event',name,'with arg',arg,'once only'
        
    def _ignore(self,name,function):
        """Unregister the given function from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: Could use a fancy list comprehension here.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all subscriptions with the messager."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method, and an enhanced
    ignore(...) method."""
    
    def accept(self,name,function,arg=None):
        
        # We initialise subscriptions when we first need it, to avoid having an
        # __init__ method that subclasses would need to call.
        if not hasattr(self,'subscriptions'):
            self.subscriptions = []    
        
        messager._accept(name,function,arg)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,arg=None):

        if not hasattr(self,'subscriptions'):
            self.subscriptions = []    

        messager._acceptOnce(name,function,arg)
        self.subscriptions.append((name,function))
        
    def ignore(self,*args):

        if not hasattr(self,'subscriptions'):
            return

        if len(args) == 1:
            name = args[0]
            function = None
        elif len(args) == 2:
            name,function = args
        else:
            raise Exception('Wrong number of arguments to Receiver.ignore')

        if function is None:
            # Remove all of this object's function subscriptions to the given
            # message name.
            temp = []
            for subscription in self.subscriptions:
                n,f = subscription
                if n == name:
                    messager._ignore(n,f)
                else:
                    temp.append(subscription)
            self.subscriptions = temp
        else:
            # Remove the single subscription (name,function)
            messager._ignore(name,function)        
            self.subscriptions.remove((name,function))
        
    def ignoreAll(self):

        if not hasattr(self,'subscriptions'):
            return

        for subscription in self.subscriptions:
            messager._ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    pass

Revision: 6728
at June 12, 2008 14:53 by chombee


Updated Code
"""
messager.py -- a simple message-passing pattern for one-many or many-many 
               dependencies. Useful for event notifications, for example.

To send a message use the singleton messager instance:

    from messager import messager

    messager.send('message name',argument)

You can pass a single argument with a message, and this argument can be anything
you like. For example, event objects that simply hold a number of attributes can
be constrcuted and passed as arguments with messages.
    
Messager maintains a mapping of message names to lists of functions (and their
arguments). When a message is sent, all of the functions subscribed to that
message are called and passed the argument given when the function was
subscribed followed by argument given when the message was sent. To subscribe a
function you can just call the messager directly again:

    messager.accept('message name',function,argument)

    messager.acceptOnce('message name',function,argument)

Functions that accept messages must accept two arguments, the one from the
accepter and the one from the sender, although either of these may be None if
the accepter or the sender did not pass an argument:

    def my_receiver_function(self,acceptersArg,sendersArg):
        ...

To unsubscribe a function from a message name use ignore:

    messager.ignore('message name',function)

You can unsubscribe _all_ functions with:

    messager.clear()
    
Receiver is a convenience class for classes that want to accept messages, it
provides an ignoreAll() and an enhanced ignore(...) method that work as long as
you always use self.accept(...) and self.acceptOnce(...) instead of calling 
messager directly.

    # Unsubscribe all functions that this object has subscribed to any message
    # name.
    self.ignoreAll()

    # Unsubscribe all functions that this object has subscribed to the given
    # message name.
    self.ignore('message name')
    
You don't need to call Receiver.__init__() when you subclass Receiver, it has no
__init__. Receiver works by maintaining a list of the message subscriptions you
have made.

If you do `messager.verbose = True` the messager will print whenever it
receives a message or subscription, and if you do `print messager` the messager
will print out a list of all the registered message names and their subscribers.

One last thing to be aware of is that messager keeps references to (functions
of) all objects that subscribe to accept messages. For an object to be deleted
it must unsubscribe all of its functions from all messages (the ignoreAll()
method will do this).

"""

# TODO: write good test code.

class Messager:
    """Singleton messager object."""
   
    def __init__(self):
        """Initialise the dictionary mapping message names to lists of receiver 
        functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    def send(self,name,arg=None):
        """Send a message with the given name and the given argument. All
        functions registered as receivers of this message name will be
        called."""
        
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                receiver['function'](receiver['arg'],arg)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                receiver['function'](receiver['arg'],arg)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def accept(self,name,function,arg=None):
        """Register with the messager to receive messages with the given name,
        messager will call the given function to notify of a message. The arg
        object given to accept will be passed to the given function first,
        followed by the arg object given to send by the sender object."""

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'arg':arg})

        if self.verbose:
            print '',function,'subscribed to event',name,'with arg',arg
        
    def acceptOnce(self,name,function,arg=None):
        """Register to receive the next instance only of a message with the
        given name."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'arg':arg})

        if self.verbose:
            print '',function,'subscribed to event',name,'with arg',arg,'once only'
        
    def ignore(self,name,function):
        """Unregister the given function from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: Could use a fancy list comprehension here.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all subscriptions with the messager."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method, and an enhanced
    ignore(...) method."""
    
    def accept(self,name,function,arg=None):
        
        # We initialise subscriptions when we first need it, to avoid having an
        # __init__ method that subclasses would need to call.
        if not hasattr(self,'subscriptions'):
            self.subscriptions = []    
        
        messager.accept(name,function,arg)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,arg=None):

        if not hasattr(self,'subscriptions'):
            self.subscriptions = []    

        messager.acceptOnce(name,function,arg)
        self.subscriptions.append((name,function))
        
    def ignore(self,*args):

        if not hasattr(self,'subscriptions'):
            return

        if len(args) == 1:
            name = args[0]
            function = None
        elif len(args) == 2:
            name,function = args
        else:
            raise Exception('Wrong number of arguments to Receiver.ignore')

        if function is None:
            # Remove all of this object's function subscriptions to the given
            # message name.
            temp = []
            for subscription in self.subscriptions:
                n,f = subscription
                if n == name:
                    messager.ignore(n,f)
                else:
                    temp.append(subscription)
            self.subscriptions = temp
        else:
            # Remove the single subscription (name,function)
            messager.ignore(name,function)        
            self.subscriptions.remove((name,function))
        
    def ignoreAll(self):

        if not hasattr(self,'subscriptions'):
            return

        for subscription in self.subscriptions:
            messager.ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    pass

Revision: 6727
at June 12, 2008 10:25 by chombee


Updated Code
"""
messager.py -- a simple message-passing pattern for one-many or many-many 
               dependencies. Useful for event notifications, for example.

To send a message use the singleton messager instance:

    from messager import messager

    messager.send('message name',['list','of','arguments'])
    
Messager maintains a mapping of message names to lists of functions (and their
arguments). When a message is sent, all of the functions subscribed to that
message are called with the list of arguments given when the function was
subscribed followed by the list of arguments given when the message was sent. To
subscribe a function you can just call the messager directly again:

    messager.accept('message name',function,['list','of','arguments'])

    messager.acceptOnce('message name',function,['list','of','arguments'])

To unsubscribe a function from a message name use ignore:

    messager.ignore('message name',function)

You can unsubscribe _all_ functions with:

    messager.clear()
    
Receiver is a convenience class for classes that want to accept messages, it
provides an ignoreAll() method that works as long as you always use
self.accept(...) and self.acceptOnce(...) instead of calling messager directly.

    # Unsubscribe all functions that this object-instance has subscribed to any
    # message name.
    self.ignoreAll()
    
You don't need to call Receiver.__init__() when you subclass Receiver, it has no
__init__. Receiver works by maintaining a list of the message subscriptions you
have made.

If you do `messager.verbose = True` the messager will print whenever it
receives a message or subscription, and if you do `print messager` the messager
will print out a list of all the registered message names and their subscribers.

One last thing to be aware of is that messager keeps references to all objects 
that register to accept messages. For an object to be deleted it must 
unregister itself from all messages (the ignoreAll() method is useful for this).

"""

# TODO: test args. Write better test code.

class Messager:
    """Singleton messager object, known by everyone."""
   
    def __init__(self):
        """Messager keeps a dictionary mapping message names to lists of 
        receiver functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    # FIXME? I think it might be more Pythonic to use a message object rather
    # than a list of args as the value of a message.
    def send(self,name,args=[]):
        """Send a message with the given name and the given args. All functions
        registered as receivers of this message name will be called."""
        
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def accept(self,name,function,args=[]):
        """Register with the messager to receive messages with the given name,
        messager will call the given function to notify of a message. The list
        of args given to accept will be passed to the given function first,
        followed by the list of args given to send by the sender object. """

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args
        
    def acceptOnce(self,name,function,args=[]):
        """Register to receive the next instance of a message with the given 
        name only."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args,'once only'
        
    def ignore(self,name,function):
        """Unregister the given function object from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all registrations with the messager."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method."""
    
    def accept(self,name,function,args=[]):
        
        # We initialise subscriptions when we first need it, to avoid having an
        # __init__ method that subclasses would need to call.
        if not hasattr(self,subscriptions):
            self.subscriptions = []    
        
        messager.accept(name,function,args)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,args=[]):

        if not hasattr(self,subscriptions):
            self.subscriptions = []    

        messager.acceptOnce(name,function,args)
        self.subscriptions.append((name,function))
        
    def ignore(self,name,function):

        if not hasattr(self,subscriptions):
            return

        messager.ignore(name,function)        
        self.subscriptions.remove((name,function))
    
    def ignoreAll(self):

        if not hasattr(self,subscriptions):
            return

        for subscription in self.subscriptions:
            messager.ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    # This is a stupid unit test. Write a proper one.

    def foo(*args):
        print 'foo'
        
    def bar(*args):
        print 'bar'
    
    def gar(*args):
        print 'gar'

    def rar(*args):
        print 'rar'
        
    print 'Testing Messager'    
    messager.verbose = True
    messager.accept('foobar',foo)
    print messager
    messager.accept('foobar',bar)
    print messager
    messager.send('foobar')
    messager.acceptOnce('garrar',gar)
    messager.accept('garrar',rar)
    messager.send('garrar')
    messager.send('garrar')
    messager.ignore('garrar',rar)
    messager.ignore('foobar',foo)
    messager.send('garrar')
    messager.send('foobar')
    
    print
    print 'Testing Receiver'
    receiver = Receiver()
    receiver.accept('foo',foo)
    receiver.accept('foo',bar)
    receiver.accept('gar',gar)
    receiver.acceptOnce('gar',rar)
    messager.send('foo')
    messager.send('gar')
    messager.send('gar')
    receiver.ignore('foo',bar)
    messager.send('foo')
    messager.send('gar')
    receiver.ignoreAll()
    messager.send('foo')
    messager.send('gar')

Revision: 6726
at June 11, 2008 19:11 by chombee


Updated Code
# TODO: test args. Write better test code.

class Messager:
    """Singleton messager object, known by everyone."""
   
    def __init__(self):
        """Messager keeps a dictionary mapping message names to lists of 
        receiver functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    # FIXME? I think it might be more Pythonic to use a message object rather
    # than a list of args as the value of a message.
    def send(self,name,args=[]):
        """Send a message with the given name and the given args. All functions
        registered as receivers of this message name will be called."""
        
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def accept(self,name,function,args=[]):
        """Register with the messager to receive messages with the given name,
        messager will call the given function to notify of a message. The list
        of args given to accept will be passed to the given function first,
        followed by the list of args given to send by the sender object. """

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args
        
    def acceptOnce(self,name,function,args=[]):
        """Register to receive the next instance of a message with the given 
        name only."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args,'once only'
        
    def ignore(self,name,function):
        """Unregister the given function object from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all registrations with the messager."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method."""

    def __init__(self):
        self.subscriptions = []
    
    def accept(self,name,function,args=[]):
        messager.accept(name,function,args)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,args=[]):
        messager.acceptOnce(name,function,args)
        self.subscriptions.append((name,function))
        
    def ignore(self,name,function):
        messager.ignore(name,function)        
        self.subscriptions.remove((name,function))
    
    def ignoreAll(self):
        for subscription in self.subscriptions:
            messager.ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    def foo(*args):
        print 'foo'
        
    def bar(*args):
        print 'bar'
    
    def gar(*args):
        print 'gar'

    def rar(*args):
        print 'rar'
        
    print 'Testing Messager'    
    messager.verbose = True
    messager.accept('foobar',foo)
    print messager
    messager.accept('foobar',bar)
    print messager
    messager.send('foobar')
    messager.acceptOnce('garrar',gar)
    messager.accept('garrar',rar)
    messager.send('garrar')
    messager.send('garrar')
    messager.ignore('garrar',rar)
    messager.ignore('foobar',foo)
    messager.send('garrar')
    messager.send('foobar')
    
    print
    print 'Testing Receiver'
    receiver = Receiver()
    receiver.accept('foo',foo)
    receiver.accept('foo',bar)
    receiver.accept('gar',gar)
    receiver.acceptOnce('gar',rar)
    messager.send('foo')
    messager.send('gar')
    messager.send('gar')
    receiver.ignore('foo',bar)
    messager.send('foo')
    messager.send('gar')
    receiver.ignoreAll()
    messager.send('foo')
    messager.send('gar')

Revision: 6725
at June 11, 2008 19:10 by chombee


Updated Code
# TODO: test args.

class Messager:
    """Singleton messager object, known by everyone."""
   
    def __init__(self):
        """Messager keeps a dictionary mapping message names to lists of 
        receiver functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    # FIXME? I think it might be more Pythonic to use a message object rather
    # than a list of args as the value of a message.
    def send(self,name,args=[]):
        """Send a message with the given name and the given args. All functions
        registered as receivers of this message name will be called."""
        
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def accept(self,name,function,args=[]):
        """Register with the messenger to receive messages with the given name,
        messenger will call the given function to notify of a message. The list
        of args given to accept will be passed to the given function first,
        followed by the list of args given to send by the sender object. """

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args
        
    def acceptOnce(self,name,function,args=[]):
        """Register to receive the next instance of a message with the given 
        name only."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args,'once only'
        
    def ignore(self,name,function):
        """Unregister the given function object from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all registrations with the messenger."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method."""

    def __init__(self):
        self.subscriptions = []
    
    def accept(self,name,function,args=[]):
        messager.accept(name,function,args)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,args=[]):
        messager.acceptOnce(name,function,args)
        self.subscriptions.append((name,function))
        
    def ignore(self,name,function):
        messager.ignore(name,function)        
        self.subscriptions.remove((name,function))
    
    def ignoreAll(self):
        for subscription in self.subscriptions:
            messager.ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    def foo(*args):
        print 'foo'
        
    def bar(*args):
        print 'bar'
    
    def gar(*args):
        print 'gar'

    def rar(*args):
        print 'rar'
        
    print 'Testing Messager'    
    messager.verbose = True
    messager.accept('foobar',foo)
    print messager
    messager.accept('foobar',bar)
    print messager
    messager.send('foobar')
    messager.acceptOnce('garrar',gar)
    messager.accept('garrar',rar)
    messager.send('garrar')
    messager.send('garrar')
    messager.ignore('garrar',rar)
    messager.ignore('foobar',foo)
    messager.send('garrar')
    messager.send('foobar')
    
    print
    print 'Testing Receiver'
    receiver = Receiver()
    receiver.accept('foo',foo)
    receiver.accept('foo',bar)
    receiver.accept('gar',gar)
    receiver.acceptOnce('gar',rar)
    messager.send('foo')
    messager.send('gar')
    messager.send('gar')
    receiver.ignore('foo',bar)
    messager.send('foo')
    messager.send('gar')
    receiver.ignoreAll()
    messager.send('foo')
    messager.send('gar')

Revision: 6724
at June 11, 2008 19:08 by chombee


Updated Code
# TODO: args.

class Messager:
    """Singleton messager object, known by everyone."""
   
    def __init__(self):
        """Messager keeps a dictionary mapping message names to lists of 
        receiver functions."""

        self.receivers = {}
        self.one_time_receivers = {}
        self.verbose = False

    # FIXME? I think it might be more Pythonic to use a message object rather
    # than a list of args as the value of a message.
    def send(self,name,args=[]):
        """Send a message with the given name and the given args. All functions
        registered as receivers of this message name will be called."""
        
        if self.verbose:
            print 'Sending message',name
        
        if self.receivers.has_key(name):
            for receiver in self.receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
        
        if self.one_time_receivers.has_key(name):
            for receiver in self.one_time_receivers[name]:
                receiver['function'](receiver['args'],args)
                if self.verbose:
                    print '   received by',receiver['function']
            del self.one_time_receivers[name]
    
    def accept(self,name,function,args=[]):
        """Register with the messenger to receive messages with the given name,
        messenger will call the given function to notify of a message. The list
        of args given to accept will be passed to the given function first,
        followed by the list of args given to send by the sender object. """

        if not self.receivers.has_key(name):
            self.receivers[name] = []
        self.receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args
        
    def acceptOnce(self,name,function,args=[]):
        """Register to receive the next instance of a message with the given 
        name only."""

        if not self.one_time_receivers.has_key(name):
            self.one_time_receivers[name] = []
        self.one_time_receivers[name].append({'function':function,'args':args})

        if self.verbose:
            print '',function,'subscribed to event',name,'with args',args,'once only'
        
    def ignore(self,name,function):
        """Unregister the given function object from the given message name."""

        if self.receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.receivers[name] = temp

        if self.one_time_receivers.has_key(name):        
            # FIXME: this isn't very efficient.
            temp = []
            for receiver in self.one_time_receivers[name]:
                if receiver['function'] != function:
                    temp.append(receiver)            
            self.one_time_receivers[name] = temp

        if self.verbose:
            print '',function,'unsubscribed from',name             
    
    def clear(self):
        """Clear all registrations with the messenger."""
        
        self.receivers = {}
        self.one_time_receivers = {}
        
    def __str__(self):
        """Return a string showing which functions are registered with 
        which event names, useful for debugging."""
        
        string = 'Receivers:\n'
        string += self.receivers.__str__() + '\n'
        string += 'One time receivers:\n'
        string += self.one_time_receivers.__str__()
        return string

# Create the single instance of Messager.
messager = Messager()

class Receiver:
    """A class to inherit if you want to register with the messager to receive
    messages. You don't have to inherit this to register for messages, you can
    just call messager directly, but this class maintains a list of your message
    subscriptions and provides a handy ignoreAll() method."""

    def __init__(self):
        self.subscriptions = []
    
    def accept(self,name,function,args=[]):
        messager.accept(name,function,args)
        self.subscriptions.append((name,function))
        
    def acceptOnce(self,name,function,args=[]):
        messager.acceptOnce(name,function,args)
        self.subscriptions.append((name,function))
        
    def ignore(self,name,function):
        messager.ignore(name,function)        
        self.subscriptions.remove((name,function))
    
    def ignoreAll(self):
        for subscription in self.subscriptions:
            messager.ignore(*subscription)
        self.subscriptions = []
                  
if __name__ == '__main__':

    def foo(*args):
        print 'foo'
        
    def bar(*args):
        print 'bar'
    
    def gar(*args):
        print 'gar'

    def rar(*args):
        print 'rar'
        
    print 'Testing Messager'    
    messager.verbose = True
    messager.accept('foobar',foo)
    print messager
    messager.accept('foobar',bar)
    print messager
    messager.send('foobar')
    messager.acceptOnce('garrar',gar)
    messager.accept('garrar',rar)
    messager.send('garrar')
    messager.send('garrar')
    messager.ignore('garrar',rar)
    messager.ignore('foobar',foo)
    messager.send('garrar')
    messager.send('foobar')
    
    print
    print 'Testing Receiver'
    receiver = Receiver()
    receiver.accept('foo',foo)
    receiver.accept('foo',bar)
    receiver.accept('gar',gar)
    receiver.acceptOnce('gar',rar)
    messager.send('foo')
    messager.send('gar')
    messager.send('gar')
    receiver.ignore('foo',bar)
    messager.send('foo')
    messager.send('gar')
    receiver.ignoreAll()
    messager.send('foo')
    messager.send('gar')

Revision: 6723
at June 11, 2008 07:07 by chombee


Updated Code
"""Messager pattern from Panda3D. This is somewhat similar to GoF's Mediator and
Observer patterns, but not the same. Use this when you have a lot of objects
spread throughout the class hierarchy that need to communicate with each other.
It's a way of implementing one-to-many or many-to-many communication, in which
an object can send a message to many receivers without needing to know who those
receivers are, or how many receivers there are, or even if there are any
receivers (although an object can potentially check all these things if it wants
to). The singleton messager object receives messages from broadcaster objects
and forwards them to receiver objects. Setup a single, simple, generic, system-
wide messaging system, instead of creating different systems for each different
type of message or messaging relation.

The disadvantage of just implementing this pattern once and using it everywhere 
is that with lots of senders and receivers it might become difficult to 
understand and debug message-based behaviour. Particularly if the order in 
which messages are sent and received becomes important, when you send a message 
with this pattern there's no way of knowing in what order the receiver objects 
will receive the message, and therefore know way of knowing in what order their 
responses will be executed, and those responses may include sending more 
messages...

Also you have to be careful to avoid clashes in message names.

The GoF Mediator and Observer patterns are similar in that they enable a mode 
of communication between objects in which the sender object broadcasts a 
message without needing to know all of the receiver objects, and the receiver 
objects can receive messages without being tightly coupled to the sender 
object(s).

In the GoF Mediator pattern, the Mediator object is more than just a messager 
that passes messages between other objects and leaves the behaviour up to the 
others. The mediator actually encapsulates the interaction behaviour between 
objects, deciding which methods to call on which objects each time it receives 
a notification from an object. Mediator centralises control of how objects 
cooperate in the mediator class, whereas the Messager pattern leaves this 
control distributed throughout the objects themselves.

In the GoF observer pattern a one-to-many dependency is setup, there is no 
separate Messager object but rather the sender object itself maintains the list 
of reciever object and notifies them of new messages. With its separate 
messager object the Messager pattern enables many-to-many dependencies as well 
as one-to-many.

One last thing to be aware of is that Messager keeps references to all objects 
that register to accept messages. For an object to be deleted it must 
unregister itself from all messages.

"""
class Messager(Singleton):
	"""Singleton messager object, known by everyone."""

	def __init__(self):
		"""Messager keeps a dictionary mapping message names to lists of 
		receiver functions."""

		self.receivers = {}
		self.one_time_receivers = {}
		self.verbose = False

        # I think it might be more Pythonic to use a message object rather than 
        # a list of args as the value of a message.
	def send(self,name,args=[]):
		"""Send a message with the given name and the given args. All functions
		registered as receivers of this message name will be called."""
		
		...

	def accept(name,function,args=[]):
		"""Register with the messenger to receive messages with the given name,
		messenger will call the given function to notify of a message. The list
		of args given to accept will be passed to the given function first,
		followed by the list of args given to send by the sender object. """

		...
		
	def acceptOnce(name,function):
		"""Register to receive the next instance of a message with the given 
		name only."""

		...
		
	def ignore(name,function):
		"""Unregister the given function object from the given message name."""

		...
		
	def ignoreAll(self, ??):
	    # ignoreAll() is supposed to allow an object to unregister from all 
	    # messages it is registered to. You can't do that though because
	    # Messager doesn't know which object is registered, only which function.
	    # Either Messager needs to keep track of objects, or define a Receiver
	    # base class that keeps track of all registrations made by the object
	    # just for the purpose of enabling ignoreAll.
	    pass

	def verbose(self,on_off):
		"""Enable or disable verbose mode. (In verbose mode, Messager will print 
		out the details of each call to accept, send, ignore, etc.)"""
		self.verbose = on_off

	def clear(self):
		"""Clear all registrations with the messenger."""
		self.receivers = {}
		self.one_time_receivers = {}
		
	def __str__(self):
		"""Return a string showing which functions are registered with 
		which event names, useful for debugging."""
		
		string = 'Receivers:\n'
		string += self.receivers.__str__()
		string = 'One time receivers:\n'
		string += self.one_time_receivers.__str__()

Revision: 6722
at June 11, 2008 07:05 by chombee


Updated Code
"""Messager pattern from Panda3D. This is somewhat similar to GoF's Mediator and
Observer patterns, but not the same. Use this when you have a lot of objects
spread throughout the class hierarchy that need to communicate with each other.
It's a way of implementing one-to-many or many-to-many communication, in which
an object can send a message to many receivers without needing to know who those
receivers are, or how many receivers there are, or even if there are any
receivers (although an object can potentially check all these things if it wants
to). The singleton messager object receives messages from broadcaster objects
and forwards them to receiver objects. Setup a single, simple, generic, system-
wide messaging system, instead of creating different systems for each different
type of message or messaging relation.

The disadvantage of just implementing this pattern once and using it everywhere 
is that with lots of senders and receivers it might become difficult to 
understand and debug message-based behaviour. Particularly if the order in 
which messages are sent and received becomes important, when you send a message 
with this pattern there's no way of knowing in what order the receiver objects 
will receive the message, and therefore know way of knowing in what order their 
responses will be executed, and those responses may include sending more 
messages...

Also you have to be careful to avoid clashes in message names.

The GoF Mediator and Observer patterns are similar in that they enable a mode 
of communication between objects in which the sender object broadcasts a 
message without needing to know all of the receiver objects, and the receiver 
objects can receive messages without being tightly coupled to the sender 
object(s).

In the GoF Mediator pattern, the Mediator object is more than just a messager 
that passes messages between other objects and leaves the behaviour up to the 
others. The mediator actually encapsulates the interaction behaviour between 
objects, deciding which methods to call on which objects each time it receives 
a notification from an object. Mediator centralises control of how objects 
cooperate in the mediator class, whereas the Messager pattern leaves this 
control distributed throughout the objects themselves.

In the GoF observer pattern a one-to-many dependency is setup, there is no 
separate Messager object but rather the sender object itself maintains the list 
of reciever object and notifies them of new messages. With its separate 
messager object the Messager pattern enables many-to-many dependencies as well 
as one-to-many.

One last thing to be aware of is that Messager keeps references to all objects 
that register to accept messages. For an object to be deleted it must 
unregister itself from all messages.

"""
class Messager(Singleton):
	"""Singleton messager object, known by everyone."""

	def __init__(self):
		"""Messager keeps a dictionary mapping message names to lists of 
		receiver functions."""

		self.receivers = {}
		self.one_time_receivers = {}
		self.verbose = False

    # I think it might be more Pythonic to use a message object rather than a
    # list of args as the value of a message.
	def send(self,name,args=[]):
		"""Send a message with the given name and the given args. All functions
		registered as receivers of this message name will be called."""
		
		...

	def accept(name,function,args=[]):
		"""Register with the messenger to receive messages with the given name,
		messenger will call the given function to notify of a message. The list
		of args given to accept will be passed to the given function first,
		followed by the list of args given to send by the sender object. """

		...
		
	def acceptOnce(name,function):
		"""Register to receive the next instance of a message with the given 
		name only."""

		...
		
	def ignore(name,function):
		"""Unregister the given function object from the given message name."""

		...
		
	def ignoreAll(self, ??):
	    # ignoreAll() is supposed to allow an object to unregister from all 
	    # messages it is registered to. You can't do that though because
	    # Messager doesn't know which object is registered, only which function.
	    # Either Messager needs to keep track of objects, or define a Receiver
	    # base class that keeps track of all registrations made by the object
	    # just for the purpose of enabling ignoreAll.
		pass

	def verbose(self,on_off):
		"""Enable or disable verbose mode. (In verbose mode, Messager will print 
		out the details of each call to accept, send, ignore, etc.)"""
		self.verbose = on_off

	def clear(self):
		"""Clear all registrations with the messenger."""
		self.receivers = {}
		self.one_time_receivers = {}
		
	def __str__(self):
		"""Return a string showing which functions are registered with 
		which event names, useful for debugging."""
		
		string = 'Receivers:\n'
		string += self.receivers.__str__()
		string = 'One time receivers:\n'
		string += self.one_time_receivers.__str__()

Revision: 6721
at June 11, 2008 07:03 by chombee


Initial Code
"""Messager pattern from Panda3D. This is somewhat similar to GoF's Mediator and
Observer patterns, but not the same. Use this when you have a lot of objects
spread throughout the class hierarchy that need to communicate with each other.
It's a way of implementing one-to-many or many-to-many communication, in which
an object can send a message to many receivers without needing to know who those
receivers are, or how many receivers there are, or even if there are any
receivers (although an object can potentially check all these things if it wants
to). The singleton messager object receives messages from broadcaster objects
and forwards them to receiver objects. Setup a single, simple, generic, system-
wide messaging system, instead of creating different systems for each different
type of message or messaging relation.

The disadvantage of just implementing this pattern once and using it everywhere is that with lots of senders and receivers it might become difficult to understand and debug message-based behaviour. Particularly if the order in which messages are sent and received becomes important, when you send a message with this pattern there's no way of knowing in what order the receiver objects will receive the message, and therefore know way of knowing in what order their responses will be executed, and those responses may include sending more messages...

The GoF Mediator and Observer patterns are similar in that they enable a mode of communication between objects in which the sender object broadcasts a message without needing to know all of the receiver objects, and the receiver objects can receive messages without being tightly coupled to the sender object(s).

In the GoF Mediator pattern, the Mediator object is more than just a messager that passes messages between other objects and leaves the behaviour up to the others. The mediator actually encapsulates the interaction behaviour between objects, deciding which methods to call on which objects each time it receives a notification from an object. Mediator centralises control of how objects cooperate in the mediator class, whereas the Messager pattern leaves this control distributed throughout the objects themselves.

In the GoF observer pattern a one-to-many dependency is setup, there is no separate Messager object but rather the sender object itself maintains the list of reciever object and notifies them of new messages. With its separate messager object the Messager pattern enables many-to-many dependencies as well as one-to-many.

One last thing to be aware of is that Messager keeps references to all objects that register to accept messages. For an object to be deleted it must unregister itself from all messages.

"""
class Messager(Singleton):
	"""Singleton messager object, known by everyone."""

	def __init__(self):
		"""Messager keeps a dictionary mapping message names to lists of 
		receiver functions."""

		self.receivers = {}
		self.one_time_receivers = {}
		self.verbose = False

    # I think it might be more Pythonic to use a message object rather than a
    # list of args as the value of a message.
	def send(self,name,args=[]):
		"""Send a message with the given name and the given args. All functions
		registered as receivers of this message name will be called."""
		
		...

	def accept(name,function,args=[]):
		"""Register with the messenger to receive messages with the given name,
		messenger will call the given function to notify of a message. The list
		of args given to accept will be passed to the given function first,
		followed by the list of args given to send by the sender object. """

		...
		
	def acceptOnce(name,function):
		"""Register to receive the next instance of a message with the given 
		name only."""

		...
		
	def ignore(name,function):
		"""Unregister the given function object from the given message name."""

		...
		
	def ignoreAll(self, ??):
	    # ignoreAll() is supposed to allow an object to unregister from all 
	    # messages it is registered to. You can't do that though because
	    # Messager doesn't know which object is registered, only which function.
	    # Either Messager needs to keep track of objects, or define a Receiver
	    # base class that keeps track of all registrations made by the object
	    # just for the purpose of enabling ignoreAll.
		pass

	def verbose(self,on_off):
		"""Enable or disable verbose mode. (In verbose mode, Messager will print 
		out the details of each call to accept, send, ignore, etc.)"""
		self.verbose = on_off

	def clear(self):
		"""Clear all registrations with the messenger."""
		self.receivers = {}
		self.one_time_receivers = {}
		
	def __str__(self):
		"""Return a string showing which functions are registered with 
		which event names, useful for debugging."""
		
		string = 'Receivers:\n'
		string += self.receivers.__str__()
		string = 'One time receivers:\n'
		string += self.one_time_receivers.__str__()

Initial URL


Initial Description
This is my implementation of the messager pattern used by Panda3D for event
handling. It's a really nice idea but I think Panda's version is implemented
weirdly, it uses the subscribe objects as keys in the dictionary. That means
that if you want more than one object to subscribe to the same message you
_have_ to subclass DirectObject and use self.accept(...), can't just call the
messager directly or you'll get a different behaviour, it means that a single
object instance can't subscribe two different functions to the same message, and
it means that your _objects_ have to be _hashable_ (!) because the messager uses
them as keys in a dictionary. It seems to me that it would be much simpler if
the messager's dictionary just mapped message names to function objects, so
that's what I did.

Use this pattern when you have a lot of objects spread throughout the class
hierarchy that need to communicate with each other. It's a way of implementing
one-to-many or many-to-many communication, in which an object can send a message
to many receivers without needing to know who those receivers are, or how many
receivers there are, or even if there are any receivers (although an object can
potentially check all these things if it wants to). The singleton messager
object receives messages from broadcaster objects and forwards them to receiver
objects. Setup a single, simple, generic, system- wide messaging system, instead
of creating different systems for each different type of message or messaging
relation.

The disadvantage of just implementing this pattern once and using it everywhere
is that with lots of senders and receivers it might become difficult to
understand and debug message-based behaviour. Particularly if the order in which
messages are sent and received becomes important, when you send a message with
this pattern there's no way of knowing in what order the receiver objects will
receive the message, and therefore know way of knowing in what order their
responses will be executed, and those responses may include sending more
messages. You can implement some ordering relations by having an object receive
a message, deal with it, then send a different message to other recievers, but I
wouldn't want to overuse that.

Also you have to be careful to avoid clashes in message names.

The GoF Mediator and Observer patterns are similar in that they enable a mode 
of communication between objects in which the sender object broadcasts a 
message without needing to know all of the receiver objects, and the receiver 
objects can receive messages without being tightly coupled to the sender 
object(s).

In the GoF Mediator pattern, the Mediator object is more than just a messager 
that passes messages between other objects and leaves the behaviour up to the 
others. The mediator actually encapsulates the interaction behaviour between 
objects, deciding which methods to call on which objects each time it receives 
a notification from an object. Mediator centralises control of how objects 
cooperate in the mediator class, whereas the Messager pattern leaves this 
control distributed throughout the objects themselves.

In the GoF observer pattern a one-to-many dependency is setup, there is no 
separate Messager object but rather the sender object itself maintains the list 
of reciever object and notifies them of new messages. With its separate 
messager object the Messager pattern enables many-to-many dependencies as well 
as one-to-many.

Initial Title
Messager pattern from Panda3D

Initial Tags
python

Initial Language
Python