How can I fire a Traits static event notification on a List?
Question: I am working through the [`traits`](https://github.com/enthought/traits)
[presentation from PyCon
2010](http://python.mirocommunity.org/video/1690/pycon-2010-introduction-to-
tra). At about 2:30:45 the presenter starts covering [trait event
notifications](http://code.enthought.com/projects/traits/docs/html/traits_user_manual/notification.html),
which allow (among other things) the ability to automatically call a
subroutine any time a [`trait`](https://github.com/enthought/traits) has
changed.

I am running a modified copy of the example he gave... In this trial, I am
trying to see whether I can fire a static event whenever I make a change to
`volume` or `inputs`.

    
    
    from traits.api import HasTraits, Range, List, Float
    import traits
    class Amplifier(HasTraits):
        """
        Define an Amplifier (a la Spinal Tap) with Enthought's traits.  Use traits  
        to enforce values boundaries on the Amplifier's objects.  Use events to 
        notify via the console when the volume trait is changed and when new volume 
        traits are added to inputs.
        """
        volume = Range(value=5.0, trait=Float, low=0.0, high=11.0)
        inputs = List(volume)    # I want to fire a static trait event notification
                                 # when another volume element is added
    
        def __init__(self, volume=5.0):
            super(Amplifier, self).__init__()
            self.volume = volume
            self.inputs.append(volume)
    
        def _volume_changed(self, old, new):
            # static event listener for self.volume
            if not (new in self.inputs):
                self.inputs.append(self.volume)
            if new == 11.0:
                print "This one goes to eleven... so far, we have seen", self.inputs
    
        def _inputs_changed(self, old, new):
            # static event listener for self.inputs
            print "Check it out!!"
    
    if __name__=='__main__':
        spinal_tap = Amplifier()
        spinal_tap.volume = 11.0
        print "DIRECTLY adding a new volume input..."
        spinal_tap.inputs.append(4.0)
        try:
            print "NEGATIVE Test... adding 12.0"
            spinal_tap.inputs.append(12.0)
        except  traits.trait_errors.TraitError:
            print "Test passed"
    

When I run this script, I can see `This one goes to eleven... so far, we have
seen [5.0, 11.0]` in the console output, so I know that `_volume_changed()`
gets fired when I assign `11.0` to `spinal_tap.volume`.

However, I never see any events from `_inputs_changed()`. No matter what
example I cook up, I can't get a
[`List`](http://code.enthought.com/projects/traits/docs/html/traits_user_manual/defining.html#other-
predefined-traits) to fire an event.

This is the output I am seeing... note that there is no evidence that
`_inputs_changed()` ever fires.

    
    
    [mpenning@Bucksnort ~]$ python spinaltap.py
    This one goes to eleven... so far, we have seen [5.0, 11.0]
    DIRECTLY adding a new volume input...
    NEGATIVE Test... adding 12.0
    Test passed
    [mpenning@Bucksnort ~]$
    

I have run this both under Python2.6 / Cygwin / Windows 7 and Python 2.5 /
Linux (all using [`traits`](https://github.com/enthought/traits) version 4.0.0
that I `easy_install` directly off [Enthought's site](http://enthought.com/)).
The results are the same no matter what I have tried so far.

Should a
[`List`](http://code.enthought.com/projects/traits/docs/html/traits_user_manual/defining.html#other-
predefined-traits) be able to fire a static event when using traits? If so, am
I doing something wrong?


Answer: After browsing their unit tests, I found a test for `Dict` traits in
enthought's [event unittest
coverage](https://github.com/enthought/traits/blob/master/traits/tests/container_events_test_case.py)...
it looks like when you have a container like a `Dict` or `List` that you need
to set up the magic event listener method like this:

    
    
    ## Broken method definition: def _inputs_changed(self, old, new):
    # container event static listeners must be in the form of _foo_items_changed()
    def _inputs_items_changed(self, old, new):
        # static event listener for self.inputs
        if len(new.added) > 0:
            print "Check it out, we added %s to self.items" % new.added
        elif len(new.removed) > 0:
            print "Check it out, we removed %s from self.items" % new.removed
    

Likewise, I also discovered that the `on_trait_change` decorator (used for
dynamic `traits` event notification) requires similar nomenclature if you are
calling it with a `traits.api.List` or `traits.api.Dict`... so I could also
write the code above as:

    
    
    from traits.api import on_trait_change
    # ...
    @on_trait_change('inputs_items')
    def something_changed(self, name, new):
        # static event listener for self.inputs
        if len(new.added) > 0:
            print "Check it out, we added %s to self.items" % new.added
        elif len(new.removed) > 0:
            print "Check it out, we removed %s from self.items" % new.removed
    

Either way, when I run the code, I get expected output:

    
    
    [mpenning@Bucksnort ~]$ python spinaltap.py
    Check it out, we added [5.0] to self.items
    Check it out, we added [11.0] to self.items
    This one goes to eleven... so far, we have seen [5.0, 11.0]
    DIRECTLY adding a new volume input...
    Check it out, we added [4.0] to self.items
    NEGATIVE Test... adding 12.0
    Test passed
    [mpenning@Bucksnort ~]$
    

