Prevent double-subscribes with registry decorator

This adds a simple flag to object instances to avoid multiple calls
to 'subscribe' for a class with multiple children that use the
'has_registry_receivers' decorator.

The registry manager protects from multiple subscriptions of the
same function by storing callbacks based on IDs, but this is necessary
to eliminate the duplicate debug logging statements.

Change-Id: I38e0a5f4c361435ae4af6c16a407191b1e722e37
This commit is contained in:
Kevin Benton 2017-02-27 05:13:33 -08:00
parent 3028edbf6b
commit 92116a0cc9
2 changed files with 27 additions and 0 deletions

View File

@ -80,6 +80,11 @@ def has_registry_receivers(cls):
def replacement_new(cls, *args, **kwargs):
instance = orig_new(*args, **kwargs)
if getattr(instance, '_DECORATED_METHODS_SUBSCRIBED', False):
# Avoid running this logic twice for classes inheriting other
# classes with this same decorator. Only one needs to execute
# to subscribe all decorated methods.
return instance
for name, unbound_method in inspect.getmembers(cls):
if (not inspect.ismethod(unbound_method) and
not inspect.isfunction(unbound_method)):
@ -91,6 +96,7 @@ def has_registry_receivers(cls):
for resource, event in _REGISTERED_CLASS_METHODS[func]:
# subscribe the bound method
subscribe(getattr(instance, name), resource, event)
setattr(instance, '_DECORATED_METHODS_SUBSCRIBED', True)
return instance
cls.__new__ = classmethod(replacement_new)
return cls

View File

@ -11,6 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
@ -30,6 +32,18 @@ class ObjectWithDecoratedCallback(object):
self.counter += 1
@registry.has_registry_receivers
class AnotherObjectWithDecoratedCallback(ObjectWithDecoratedCallback):
def __init__(self):
super(AnotherObjectWithDecoratedCallback, self).__init__()
self.counter2 = 0
@registry.receives(resources.NETWORK, [events.AFTER_DELETE])
def callback2(self, *args, **kwargs):
self.counter2 += 1
class CallBacksManagerTestCase(base.BaseTestCase):
def test_decorated_inst_method_receives(self):
@ -49,3 +63,10 @@ class CallBacksManagerTestCase(base.BaseTestCase):
registry.notify(resources.NETWORK, events.AFTER_DELETE, self)
self.assertEqual(4, i1.counter)
self.assertEqual(1, i2.counter)
def test_object_inheriting_others_no_double_subscribe(self):
with mock.patch.object(registry, 'subscribe') as sub:
AnotherObjectWithDecoratedCallback()
# there are 3 methods (2 in parent and one in child) and 1
# subscribes to 2 events, so we expect 4 subscribes
self.assertEqual(4, len(sub.mock_calls))