Skip to content

Commit

Permalink
Merge pull request #239 from ales-erjavec/fixes/new-signals-on-link-e…
Browse files Browse the repository at this point in the history
…nable

[FIX] signalmanager: Dispatch `Signal.New` in `send` when link is disabled
  • Loading branch information
ales-erjavec committed Jun 28, 2022
2 parents 3c38df1 + 27f654e commit ede5e44
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 26 deletions.
33 changes: 28 additions & 5 deletions orangecanvas/scheme/signalmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ def __repr__(self):
__str__ = __repr__


class _LinkExtra:
"""Extra data tracked for a SchemeLink"""
__slots__ = ("flags",)
DidScheduleNew = 1

def __init__(self, flags=0):
self.flags = flags


class SignalManager(QObject):
"""
SignalManager handles the runtime signal propagation for a :class:`.Scheme`
Expand Down Expand Up @@ -196,6 +205,8 @@ def __init__(self, parent=None, *, max_running=None, **kwargs):
# mapping a node to its current outputs
self.__node_outputs = {} # type: Dict[SchemeNode, DefaultDict[OutputSignal, _OutputState]]

#: Extra link state
self.__link_extra = defaultdict(_LinkExtra) # type: DefaultDict[SchemeLink, _LinkExtra]
self.__state = SignalManager.Running
self.__runtime_state = SignalManager.Waiting

Expand Down Expand Up @@ -406,6 +417,7 @@ def __on_link_added(self, link):
def __on_link_removed(self, link):
# type: (SchemeLink) -> None
link.enabled_changed.disconnect(self.__on_link_enabled_changed)
self.__link_extra.pop(link, None)

def eventFilter(self, recv: QObject, event: QEvent) -> bool:
etype = event.type()
Expand Down Expand Up @@ -519,15 +531,21 @@ def _id_(id):
node.title, channel.name)
state.flags &= ~_OutputState.Invalidated

links = filter(
is_enabled,
scheme.find_links(source_node=node, source_channel=channel)
)
links = scheme.find_links(source_node=node, source_channel=channel)
signals = []
for link in links:
extra = self.__link_extra[link]
links_in = scheme.find_links(sink_node=link.sink_node)
index = links_in.index(link)
signals.append(sigtype(link, value, id, index=index))
if not link.is_enabled() and not extra.flags & _LinkExtra.DidScheduleNew:
# Send Signal.New with None value. Proper update will be done
# when/if the link is re-enabled.
signal = Signal.New(link, None, id, index=index)
elif link.is_enabled():
signal = sigtype(link, value, id, index=index)
else:
continue
signals.append(signal)
link.set_runtime_state_flag(SchemeLink.Invalidated, False)

self._schedule(signals)
Expand Down Expand Up @@ -584,6 +602,11 @@ def _schedule(self, signals):
"""
self.__input_queue.extend(signals)

for sig in signals:
if isinstance(sig, Signal.New):
extra = self.__link_extra[sig.link]
extra.flags |= _LinkExtra.DidScheduleNew

for link in {sig.link for sig in signals}:
# update the SchemeLink's runtime state flags
contents = self.link_contents(link)
Expand Down
48 changes: 27 additions & 21 deletions orangecanvas/scheme/tests/test_signalmanager.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import unittest

from AnyQt.QtTest import QSignalSpy

from orangecanvas.scheme import Scheme, SchemeNode, SchemeLink
from orangecanvas.scheme import signalmanager
from orangecanvas.scheme.signalmanager import (
SignalManager, Signal, compress_signals, compress_single
)
Expand Down Expand Up @@ -34,8 +31,12 @@ def setUp(self):
add = scheme.new_node(reg.widget("add"))
scheme.new_link(zero, "value", add, "left")
scheme.new_link(one, "value", add, "right")
sm = TestingSignalManager()
sm.set_workflow(scheme)
sm.start()
self.reg = reg
self.scheme = scheme
self.signal_manager = sm

def test(self):
workflow = self.scheme
Expand Down Expand Up @@ -73,9 +74,7 @@ def test(self):

def test_add_link_disabled(self):
workflow = self.scheme
sm = TestingSignalManager()
sm.set_workflow(workflow)
sm.start()
sm = self.signal_manager
n0, n1, n2 = workflow.nodes
l0, l1 = workflow.links
workflow.remove_link(l0)
Expand All @@ -98,12 +97,27 @@ def test_add_link_disabled(self):
]
)

def test_invalidated_flags(self):
def test_link_new_dispatch_after_enable(self):
workflow = self.scheme
sm = TestingSignalManager()
sm.set_workflow(workflow)
sm.start()
sm = self.signal_manager
n0, n1, n2 = workflow.nodes
l0, l1 = workflow.links
l0.set_enabled(False)
sm.send(n0, n0.description.outputs[0], -42)
sm.send(n0, n0.description.outputs[0], 42)
self.assertSequenceEqual(
sm.pending_input_signals(n2), [Signal.New(l0, None, None, 0)]
)
sm.process_queued()
l0.set_enabled(True)
self.assertSequenceEqual(
sm.pending_input_signals(n2), [Signal.Update(l0, 42, None, 0)]
)
sm.process_queued()

def test_invalidated_flags(self):
workflow = self.scheme
sm = self.signal_manager
n0, n1, n2 = workflow.nodes[:3]
l0, l1 = workflow.links[:2]

Expand Down Expand Up @@ -149,9 +163,7 @@ def test_invalidated_flags(self):

def test_pending_flags(self):
workflow = self.scheme
sm = TestingSignalManager()
sm.set_workflow(workflow)
sm.start()
sm = self.signal_manager
n0, n1, n3 = workflow.nodes[:3]
l0, l1 = workflow.links[:2]

Expand All @@ -169,10 +181,7 @@ def test_pending_flags(self):

def test_ready_flags(self):
workflow = self.scheme
sm = TestingSignalManager()
sm.set_workflow(workflow)
sm.start()

sm = self.signal_manager
n0, n1, n3 = workflow.nodes[:3]
sm.send(n0, n0.output_channel("value"), 'hello')
sm.send(n1, n1.output_channel("value"), 'hello')
Expand All @@ -187,10 +196,7 @@ def test_ready_flags(self):

def test_start_finished(self):
workflow = self.scheme
sm = TestingSignalManager()
sm.set_workflow(workflow)
sm.start()

sm = self.signal_manager
n0, n1, n3 = workflow.nodes[:3]
start_spy = QSignalSpy(sm.started)
fin_spy = QSignalSpy(sm.finished)
Expand Down

0 comments on commit ede5e44

Please sign in to comment.