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:
parent
3028edbf6b
commit
92116a0cc9
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue