Pass receiver to :GC.subscribeDestruction explicitly
Makes the subscriber object be an explicit parameter of destruction dependency subscribe/unsibscribe method. It makes possible use of this functions from static methods and to link other objects. Targets-blueprint: dependency-driven-resource-deallocation Change-Id: I2554385bf2ebc6849f09939530a7c090fb745778
This commit is contained in:
parent
584571c3d8
commit
c47269128b
|
@ -12,60 +12,56 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from yaql.language import specs
|
||||
from yaql.language import yaqltypes
|
||||
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import helpers
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dsl.name('io.murano.system.GC')
|
||||
class GarbageCollector(object):
|
||||
|
||||
@staticmethod
|
||||
@specs.parameter('target', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('publisher', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('subscriber', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('handler', yaqltypes.String(nullable=True))
|
||||
def subscribe_destruction(target, handler=None):
|
||||
caller_context = helpers.get_caller_context()
|
||||
target_this = target.object.real_this
|
||||
receiver = helpers.get_this(caller_context)
|
||||
def subscribe_destruction(publisher, subscriber, handler=None):
|
||||
publisher_this = publisher.object.real_this
|
||||
subscriber_this = subscriber.object.real_this
|
||||
|
||||
if handler:
|
||||
receiver.type.find_single_method(handler)
|
||||
subscriber.type.find_single_method(handler)
|
||||
|
||||
dependency = GarbageCollector._find_dependency(
|
||||
target_this, receiver, handler)
|
||||
publisher_this, subscriber_this, handler)
|
||||
|
||||
if not dependency:
|
||||
dependency = {'subscriber': helpers.weak_ref(receiver),
|
||||
dependency = {'subscriber': helpers.weak_ref(subscriber_this),
|
||||
'handler': handler}
|
||||
target_this.dependencies.setdefault(
|
||||
publisher_this.dependencies.setdefault(
|
||||
'onDestruction', []).append(dependency)
|
||||
|
||||
@staticmethod
|
||||
@specs.parameter('target', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('publisher', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('subscriber', dsl.MuranoObjectParameter())
|
||||
@specs.parameter('handler', yaqltypes.String(nullable=True))
|
||||
def unsubscribe_destruction(target, handler=None):
|
||||
caller_context = helpers.get_caller_context()
|
||||
target_this = target.object.real_this
|
||||
receiver = helpers.get_this(caller_context)
|
||||
def unsubscribe_destruction(publisher, subscriber, handler=None):
|
||||
publisher_this = publisher.object.real_this
|
||||
subscriber_this = subscriber.object.real_this
|
||||
|
||||
if handler:
|
||||
receiver.type.find_single_method(handler)
|
||||
subscriber.type.find_single_method(handler)
|
||||
|
||||
dds = target_this.dependencies.get('onDestruction', [])
|
||||
dds = publisher_this.dependencies.get('onDestruction', [])
|
||||
dependency = GarbageCollector._find_dependency(
|
||||
target_this, receiver, handler)
|
||||
publisher_this, subscriber_this, handler)
|
||||
|
||||
if dependency:
|
||||
dds.remove(dependency)
|
||||
|
||||
@staticmethod
|
||||
def _find_dependency(target, subscriber, handler):
|
||||
dds = target.dependencies.get('onDestruction', [])
|
||||
def _find_dependency(publisher, subscriber, handler):
|
||||
dds = publisher.dependencies.get('onDestruction', [])
|
||||
for dd in dds:
|
||||
if dd['handler'] != handler:
|
||||
continue
|
||||
|
|
|
@ -6,6 +6,17 @@ Methods:
|
|||
Body:
|
||||
- $.find(Node)
|
||||
|
||||
foo:
|
||||
Body:
|
||||
- trace(foo)
|
||||
|
||||
destructionHandler:
|
||||
Arguments:
|
||||
- obj:
|
||||
Contract: $.class(TestGCNode).notNull()
|
||||
Body:
|
||||
- trace('Destruction of {0}'.format($obj.value))
|
||||
|
||||
.destroy:
|
||||
Body:
|
||||
- trace($.value)
|
||||
|
@ -37,8 +48,9 @@ Methods:
|
|||
:TestGCNode:
|
||||
value: B
|
||||
- $x: new($model)
|
||||
- sys:GC.subscribeDestruction($x, _handler)
|
||||
- sys:GC.subscribeDestruction($x.nodes[0], _handler)
|
||||
- sys:GC.subscribeDestruction($x, $this, _handler)
|
||||
- sys:GC.subscribeDestruction($x.nodes[0], $this, _handler)
|
||||
- sys:GC.subscribeDestruction($x.nodes[0], $x, destructionHandler)
|
||||
- $x: null
|
||||
- sys:GC.collect()
|
||||
|
||||
|
|
|
@ -53,4 +53,6 @@ class TestGC(test_case.DslTestCase):
|
|||
|
||||
def test_collect_with_subscription(self):
|
||||
self.runner.testObjectsCollectWithSubscription()
|
||||
self.assertEqual(['Destroy A', 'Destroy B', 'B', 'A'], self.traces)
|
||||
self.assertEqual(
|
||||
['Destroy A', 'Destroy B', 'Destruction of B', 'B', 'A'],
|
||||
self.traces)
|
||||
|
|
|
@ -12,11 +12,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import _weakref
|
||||
import weakref
|
||||
|
||||
import mock
|
||||
from testtools import matchers
|
||||
import unittest
|
||||
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import exceptions
|
||||
|
@ -31,57 +30,57 @@ class TestGarbageCollector(base.MuranoTestCase):
|
|||
def setUp(self):
|
||||
super(TestGarbageCollector, self).setUp()
|
||||
|
||||
self.mock_subscriber = mock.MagicMock(spec=murano_object.MuranoObject)
|
||||
self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass)
|
||||
self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod)
|
||||
self.mock_method.name = "mockHandler"
|
||||
self.mock_class.methods = mock.PropertyMock(
|
||||
return_value={"mockHandler": self.mock_method})
|
||||
self.mock_subscriber.type = self.mock_class
|
||||
self.mock_subscriber.object_id = "1234"
|
||||
subscriber = mock.MagicMock(spec=murano_object.MuranoObject)
|
||||
subscriber.real_this = subscriber
|
||||
|
||||
@mock.patch("murano.dsl.helpers.get_caller_context")
|
||||
@mock.patch("murano.dsl.helpers.get_this")
|
||||
def test_set_dd(self, this, caller_context):
|
||||
this.return_value = self.mock_subscriber
|
||||
target_0 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
|
||||
target_0.object = self.mock_subscriber
|
||||
target_0.object.real_this = self.mock_subscriber
|
||||
target_0.object.dependencies = {}
|
||||
mock_class = mock.MagicMock(spec=murano_type.MuranoClass)
|
||||
mock_method = mock.MagicMock(spec=murano_method.MuranoMethod)
|
||||
mock_method.name = "mockHandler"
|
||||
mock_class.methods = mock.PropertyMock(
|
||||
return_value={"mockHandler": mock_method})
|
||||
|
||||
def find_single_method(name):
|
||||
if name != 'mockHandler':
|
||||
raise exceptions.NoMethodFound(name)
|
||||
|
||||
mock_class.find_single_method = find_single_method
|
||||
subscriber.type = mock_class
|
||||
self.subscriber = mock.MagicMock(spec=dsl.MuranoObjectInterface)
|
||||
self.subscriber.object = subscriber
|
||||
self.subscriber.type = subscriber.type
|
||||
|
||||
publisher = mock.MagicMock(spec=murano_object.MuranoObject)
|
||||
publisher.real_this = publisher
|
||||
self.publisher = mock.MagicMock(spec=dsl.MuranoObjectInterface)
|
||||
self.publisher.object = publisher
|
||||
|
||||
def test_set_dd(self):
|
||||
self.publisher.object.dependencies = {}
|
||||
garbage_collector.GarbageCollector.subscribe_destruction(
|
||||
target_0, handler="mockHandler")
|
||||
dep = self.mock_subscriber.dependencies["onDestruction"]
|
||||
self.publisher, self.subscriber, handler="mockHandler")
|
||||
dep = self.publisher.object.dependencies["onDestruction"]
|
||||
self.assertThat(dep, matchers.HasLength(1))
|
||||
dep = dep[0]
|
||||
self.assertEqual("mockHandler", dep["handler"])
|
||||
self.assertEqual(self.mock_subscriber, dep["subscriber"]())
|
||||
self.assertEqual(self.subscriber.object, dep["subscriber"]())
|
||||
|
||||
@mock.patch("murano.dsl.helpers.get_caller_context")
|
||||
@mock.patch("murano.dsl.helpers.get_this")
|
||||
def test_unset_dd(self, this, caller_context):
|
||||
this.return_value = self.mock_subscriber
|
||||
target_1 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
|
||||
target_1.object = self.mock_subscriber
|
||||
target_1.object.real_this = self.mock_subscriber
|
||||
target_1.object.dependencies = (
|
||||
def test_unset_dd(self):
|
||||
self.publisher.object.dependencies = (
|
||||
{"onDestruction": [{
|
||||
"subscriber": _weakref.ref(self.mock_subscriber),
|
||||
"subscriber": weakref.ref(self.subscriber.object),
|
||||
"handler": "mockHandler"
|
||||
}]})
|
||||
garbage_collector.GarbageCollector.unsubscribe_destruction(
|
||||
target_1, handler="mockHandler")
|
||||
self.publisher, self.subscriber, handler="mockHandler")
|
||||
self.assertEqual(
|
||||
[], self.mock_subscriber.dependencies["onDestruction"])
|
||||
[], self.publisher.object.dependencies["onDestruction"])
|
||||
|
||||
@unittest.skip("WIP")
|
||||
@mock.patch("murano.dsl.helpers.get_caller_context")
|
||||
@mock.patch("murano.dsl.helpers.get_this")
|
||||
def test_set_wrong_handler(self, this, caller_context):
|
||||
this.return_value = self.mock_subscriber
|
||||
target_2 = mock.MagicMock(spec=dsl.MuranoObjectInterface)
|
||||
target_2.object = mock.MagicMock(murano_object.MuranoObject)
|
||||
target_2.object.dependencies = {}
|
||||
def test_set_wrong_handler(self):
|
||||
self.assertRaises(
|
||||
exceptions.NoMethodFound,
|
||||
garbage_collector.GarbageCollector.subscribe_destruction,
|
||||
target_2, handler="wrongHandler")
|
||||
self.publisher, self.subscriber, handler="invalidHandler")
|
||||
self.assertRaises(
|
||||
exceptions.NoMethodFound,
|
||||
garbage_collector.GarbageCollector.unsubscribe_destruction,
|
||||
self.publisher, self.subscriber, handler="invalidHandler")
|
||||
|
|
Loading…
Reference in New Issue