Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvements to is_active ping logic #2450

Merged
merged 3 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ Specviz2d
Other Changes and Additions
---------------------------

- Improved logic for handling active state of plugins. [#2386, #2450]

- API framework for batch aperture photometry. [#2401]

3.6.3 (unreleased)
Expand Down
19 changes: 11 additions & 8 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,11 @@
# toggles before any *other* messages are received)
# if the method returns False, then the method is not considered to have fully run
# and so is NOT added to the skip list
ret_ = meth(self, msg)
if ret_ is not False and meth.__name__ not in self._methods_skip_since_last_active: # noqa
if meth.__name__ not in self._methods_skip_since_last_active:
self._methods_skip_since_last_active.append(meth.__name__)
ret_ = meth(self, msg)
if ret_ is False:
self._methods_skip_since_last_active.remove(meth.__name__)

Check warning on line 244 in jdaviz/core/template_mixin.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/core/template_mixin.py#L244

Added line #L244 was not covered by tests
return ret_

return wrapper
Expand All @@ -262,6 +264,11 @@
# _inactive_thread: thread checking for alive pings to control plugin_opened
self._inactive_thread = None
self._ping_timestamp = 0
# _ping_delay_ms should match value in setTimeout in tray_plugin.vue
# NOTE: could control with a traitlet, but then would need to pass through each
# <j-tray-plugin> component
self._ping_delay_ms = 200

# _methods_skip_since_last_active: methods that should be skipped when is_active is next
# set to True because no changes have been made. This can be used to prevent queuing
# of expensive method calls, especially when the browser throttles the ping resulting
Expand Down Expand Up @@ -298,16 +305,12 @@
self._inactive_thread.start()

def _watch_active(self):
# expected_delay_ms should match value in setTimeout in tray_plugin.vue
# NOTE: could control with a traitlet, but then would need to pass through each
# <j-tray-plugin> component
expected_delay_ms = 200
# plugin_ping (ms) set by setTimeout in tray_plugin.vue
# time.time() is in s, so need to convert to ms
while time.time()*1000 - self._ping_timestamp < 2 * expected_delay_ms:
while time.time()*1000 - self._ping_timestamp < 2 * self._ping_delay_ms:
# at least one plugin has sent an "alive" ping within twice of the expected
# interval, wait a full (double) interval and then check again
time.sleep(2 * expected_delay_ms / 1000)
time.sleep(2 * self._ping_delay_ms / 1000)

# "alive" ping has not been received within the expected time, consider all instances
# of the plugin to be closed
Expand Down
66 changes: 66 additions & 0 deletions jdaviz/tests/test_plugin_is_active.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from traitlets import Bool, Float, observe
from time import sleep

from jdaviz import Cubeviz
from jdaviz.app import Application
from jdaviz.core.config import get_configuration
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import PluginTemplateMixin, skip_if_no_updates_since_last_active


@tray_registry('test-slow-plugin', label="Test Slow Plugin")
class SlowPlugin(PluginTemplateMixin):
"""
empty plugin with a simulated slow method
"""
template = ''
uses_active_status = Bool(True).tag(sync=True)
sleep_time = Float(0).tag(sync=True)

def __init__(self, *args, **kwargs):
# reset this manually in the test to see if slow_method ran since last manual reset
self._method_ran = False
super().__init__(*args, **kwargs)

@observe('is_active', 'sleep_time')
@skip_if_no_updates_since_last_active()
def slow_method(self, *args):
self._method_ran = True
sleep(self.sleep_time)
return


def test_is_active():
# create an instance of an app with just our fake "Slow" Plugin
config = get_configuration('cubeviz')
config['tray'] = ['test-slow-plugin']

cubeviz_app = Application(config)
cubeviz_helper = Cubeviz(cubeviz_app)

plg = cubeviz_helper.plugins['Test Slow Plugin']._obj
plg.open_in_tray()
assert plg.uses_active_status is True
# there is no actual frontend here to ping (this would be True in a notebook)
assert plg.is_active is False

# so we need to fake the pings
assert plg._ping_timestamp == 0
assert plg.sleep_time == 0
plg.vue_plugin_ping({})
# since the observe on is_active/sleep_time returns immediately, the plugin is active
assert plg.is_active is True
assert plg._ping_timestamp > 0
assert plg._method_ran is True

# if there are no pings then the plugin will reset to inactive
sleep(1)
assert plg.is_active is False

# if we set sleep_time while the plugin is inactive, the method should not fire
plg._method_ran = False
plg.sleep_time = 0.01
assert plg._method_ran is False
# but should immediately once it becomes active
plg.vue_plugin_ping({})
assert plg._method_ran is True