Commits

Olemis Lang  committed fdf642e

Trac #11148 : IEntityChangeListener : kwargs => NotificationChangeInfo.event_args

  • Participants
  • Parent commits e19e1df
  • Branches trac_t11148

Comments (0)

Files changed (1)

File t11148/t11148_r11782_IEntityChangeListener_v2.diff

 
 diff -r 8d0c3223a818 trac/core.py
 --- a/trac/core.py	Thu Apr 18 14:30:21 2013 +0000
-+++ b/trac/core.py	Wed Apr 24 17:51:53 2013 -0500
++++ b/trac/core.py	Wed Apr 24 19:11:37 2013 -0500
 @@ -165,7 +165,6 @@
  
          locals_.setdefault('_implements', []).extend(interfaces)
  implements = Component.implements
  
  
-@@ -236,3 +235,68 @@
+@@ -236,3 +235,98 @@
          with the given class will not be available.
          """
          return True
 +
 +class NotificationChangeInfo(object):
-+    def __init__(self, comment, author):
++    """Additional information about entity change.
++    """
++
++    def __init__(self, comment=None, author=None):
 +        self.comment = comment
 +        self.author = author
++        self.event_args = None
++        self.action = None
++
 +
 +class IEntityChangeListener(Interface):
 +    """Extension point interface for components that require notification
 +                       interfaces. 
 +    """
 +
-+    def entity_created(entity, changeinfo = None, **kwargs):
++    def entity_created(entity, changeinfo = None):
 +        """
 +        Called when an entity is created.
 +        """
 +
-+    def entity_changed(entity, old_values, changeinfo = None, **kwargs):
++    def entity_changed(entity, old_values, changeinfo = None):
 +        """Called when an entity is modified.
 +
 +        :param old_values: is a dictionary containing the previous values of
 +                           specific for entity type.
 +        """
 +
-+    def entity_deleted(entity, changeinfo = None, **kwargs):
++    def entity_deleted(entity, changeinfo = None):
 +        """Called when an entity is deleted."""
 +
-+    def entity_reparented(entity, changeinfo = None, **kwargs):
++    def entity_reparented(entity, changeinfo = None):
 +        """Called when an entity has been re-parented."""
 +
++    def entity_event(entity, changeinfo):
++        """Called when a custom entity event takes place.
++        
++        :param changeinfo: an instance of `NotificationChangeInfo` with
++                           - `action` set to custom event name
++                           - `event_args` set to a mapping object populated
++                                          with event-specific data.
++        """
++
++
 +class ListenerNotifier(Component):
 +    required = True
 +
++    BUILTIN_EVENTS = set(['created', 'changed', 'deleted', 'reparented'])
++
 +    def notify(self, method, **kwargs):
 +        interface = method.im_class
 +        method_name = method.__name__
 +        xp = ExtensionPoint(interface)
 +        # FIXME : Sender component rather than self. Change method signature
 +        for listener in xp.extensions(self):
-+            # FIXME : Do not copy kwargs
-+            getattr(listener, method_name)(**kwargs.copy())
++            getattr(listener, method_name)(**kwargs)
 +
 +        xp = ExtensionPoint(IEntityChangeListener)
 +        generic_listeners = xp.extensions(self)
 +        if generic_listeners:
-+            #TODO : Include `interface` and `method_name` in kwargs ?
++            #TODO : Include `interface` in kwargs ?
 +            parts = method_name.split('_', 1)
-+            method_name = 'entity_' + parts[-1]
++            action = parts[-1]
++            changeinfo = kwargs.setdefault('changeinfo', 
++                                           NotificationChangeInfo())
++            target_args = set(('changeinfo', 'entity'))
++            if action in self.BUILTIN_EVENTS:
++                method_name = 'entity_' + action
++                if action == 'changed':
++                    target_args.add('old_values')
++            else:
++                method_name = 'entity_event'
++                changeinfo.action = action
++            changeinfo.event_args = dict([k, kwargs.pop(k)]
++                                         for k in kwargs.copy()
++                                         if k not in target_args)
++
 +            for listener in generic_listeners:
-+                # FIXME : Do not copy kwargs
-+                getattr(listener, method_name)(**kwargs.copy())
++                getattr(listener, method_name)(**kwargs)
 diff -r 8d0c3223a818 trac/ticket/model.py
 --- a/trac/ticket/model.py	Thu Apr 18 14:30:21 2013 +0000
-+++ b/trac/ticket/model.py	Wed Apr 24 17:51:53 2013 -0500
++++ b/trac/ticket/model.py	Wed Apr 24 19:11:37 2013 -0500
 @@ -25,7 +25,8 @@
  from trac.attachment import Attachment
  from trac import core
          """
 diff -r 8d0c3223a818 trac/ticket/tests/model.py
 --- a/trac/ticket/tests/model.py	Thu Apr 18 14:30:21 2013 +0000
-+++ b/trac/ticket/tests/model.py	Wed Apr 24 17:51:53 2013 -0500
++++ b/trac/ticket/tests/model.py	Wed Apr 24 19:11:37 2013 -0500
 @@ -9,11 +9,11 @@
  
  from trac import core
  from trac.ticket.api import (
      IMilestoneChangeListener, ITicketChangeListener, TicketSystem
  )
-@@ -1097,6 +1097,376 @@
+@@ -1097,6 +1097,389 @@
          self.assertEqual([('Test', 0, 'Some text')], self.env.db_query(
              "SELECT name, time, description FROM version WHERE name='Test'"))
  
 +    def _handle(self, kwargs):
 +        cls = kwargs['entity'].__class__
 +        self.action.append(cls.__name__.lower() + '_' + kwargs['action'])
++
++        # Remove reference to self
++        del kwargs['self']
++        # Add action for custom events
++        changeinfo = kwargs.get('changeinfo')
++        if changeinfo is not None and changeinfo.action:
++            kwargs['action'] = changeinfo.action
++        # Add custom args and clear changeinfo
++        kwargs.update(changeinfo.event_args or {}, changeinfo=None)
 +        self.details.append(kwargs)
 +
-+    def entity_created(self, entity, changeinfo = None, **kwargs):
-+        kwargs.update(entity=entity, changeinfo=changeinfo, action='created')
++    def entity_created(self, entity, changeinfo=None):
++        kwargs = locals()
++        kwargs.update(action='created')
 +        self._handle(kwargs)
 +
-+    def entity_changed(self, entity, old_values, changeinfo = None, **kwargs):
-+        kwargs.update(entity=entity, changeinfo=changeinfo, action='changed',
-+                      old_values=old_values)
++    def entity_changed(self, entity, old_values, changeinfo = None):
++        kwargs = locals()
++        kwargs.update(action='changed')
 +        self._handle(kwargs)
 +
-+    def entity_deleted(self, entity, changeinfo = None, **kwargs):
-+        kwargs.update(entity=entity, changeinfo=changeinfo, action='deleted')
++    def entity_deleted(self, entity, changeinfo = None):
++        kwargs = locals()
++        kwargs.update(action='deleted')
 +        self._handle(kwargs)
 +
-+    def entity_reparented(self, entity, changeinfo = None, **kwargs):
-+        kwargs.update(entity=entity, changeinfo=changeinfo, action='reparented')
++    def entity_reparented(self, entity, changeinfo = None):
++        kwargs = locals()
++        kwargs.update(action='reparented')
++        self._handle(kwargs)
++
++    def entity_event(self, entity, changeinfo = None):
++        kwargs = locals()
 +        self._handle(kwargs)
 +
 +
 +        self.env = EnvironmentStub(default_data=True)
 +        self.listener = GenericEntitiesChangeListenerMock(self.env)
 +
-+    def test_custom_event_args(self):
++    def test_custom_events(self):
 +
 +        class ISomethingListener(core.Interface):
 +            """Listener interface written from scratch
 +                self.details = []
 +
 +            def _handle(self, kwargs):
++                del kwargs['self']
 +                if 'optional_arg' in kwargs and kwargs['optional_arg'] is None:
 +                    del kwargs['optional_arg']
 +                self.details.append(kwargs)
 +
 +            def something_created(self, entity, changeinfo=None):
 +                args = locals()
-+                del args['self']
 +                args['action'] = 'created'
 +                self._handle(args)
 +
 +            def something_changed(self, entity, old_values, required_arg,
 +                                  changeinfo=None, optional_arg=None):
 +                args = locals()
-+                del args['self']
 +                args['action'] = 'changed'
 +                self._handle(args)
 +
 +            def something_deleted(self, entity, changeinfo=None):
 +                args = locals()
-+                del args['self']
 +                args['action'] = 'deleted'
 +                self._handle(args)
 +
 +            def something_reparented(self, entity, changeinfo=None):
 +                args = locals()
-+                del args['self']
 +                args['action'] = 'reparented'
 +                self._handle(args)
 +
  
  def suite():
      suite = unittest.TestSuite()
-@@ -1107,6 +1477,17 @@
+@@ -1107,6 +1490,17 @@
      suite.addTest(unittest.makeSuite(MilestoneTestCase, 'test'))
      suite.addTest(unittest.makeSuite(ComponentTestCase, 'test'))
      suite.addTest(unittest.makeSuite(VersionTestCase, 'test'))