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

fix: add missing dependencies params for liveness scope and table listener hooks #291

Merged
merged 6 commits into from
Mar 1, 2024
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ docker-compose.override.yml

# Ignore temporary files created during a release
releases/

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
12 changes: 6 additions & 6 deletions docker/data/storage/notebooks/DEMO.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def hist_demo(source, column):
x=column,
nbins=bin_count,
),
[bin_count, hist_range, source, column],
{bin_count, hist_range, source, column},
)

return [
Expand Down Expand Up @@ -232,7 +232,7 @@ def order_table():
),
[],
)
t = ui.use_memo(lambda: blink_to_append_only(blink_table), [blink_table])
t = ui.use_memo(lambda: blink_to_append_only(blink_table), {blink_table})

def submit_order(order_sym, order_size, side):
publisher.add(
Expand Down Expand Up @@ -398,18 +398,18 @@ def multiwave():
f"y_tan={amplitude}*Math.tan({frequency}*x+{phase})",
]
),
[amplitude, frequency, phase],
{amplitude, frequency, phase},
)
p_sin = use_memo(
lambda: Figure().plot_xy(series_name="Sine", t=t, x="x", y="y_sin").show(), [t]
lambda: Figure().plot_xy(series_name="Sine", t=t, x="x", y="y_sin").show(), {t}
)
p_cos = use_memo(
lambda: Figure().plot_xy(series_name="Cosine", t=t, x="x", y="y_cos").show(),
[t],
{t},
)
p_tan = use_memo(
lambda: Figure().plot_xy(series_name="Tangent", t=t, x="x", y="y_tan").show(),
[t],
{t},
)

return ui.column(
Expand Down
1 change: 1 addition & 0 deletions plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,7 @@ Call a callback function or `on_update` in a `TableListener` when the table is u
use_table_listener(
table: Table,
listener: Callable[[TableUpdate, bool], None] | TableListener,
dependencies: set[Any],
description: str | None = None,
do_replay: bool = False,
replay_lock: LockType = "shared",
Expand Down
4 changes: 2 additions & 2 deletions plugins/ui/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ def monitor_changed_data(source: Table):
else:
set_changed(empty_table(0))

ui.use_table_listener(source, listener)
ui.use_table_listener(source, listener, [])

added_check = ui.checkbox(
"Show Added", isSelected=show_added, on_change=set_show_added
Expand Down Expand Up @@ -930,7 +930,7 @@ from deephaven import ui, time_table
@ui.component
def resetable_table():
table, set_table = ui.use_state(lambda: time_table("PT1s"))
handle_press = ui.use_liveness_scope(lambda _: set_table(time_table("PT1s")))
handle_press = ui.use_liveness_scope(lambda _: set_table(time_table("PT1s")), [])
return [
ui.action_button(
"Reset",
Expand Down
40 changes: 23 additions & 17 deletions plugins/ui/src/deephaven/ui/hooks/use_liveness_scope.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from __future__ import annotations

from .._internal import get_context
from .use_memo import use_memo
from .use_ref import use_ref, Ref
from typing import Callable
from deephaven.liveness_scope import LivenessScope


def use_liveness_scope(func: Callable) -> Callable:
def use_liveness_scope(func: Callable, dependencies: set) -> Callable:
"""
Wraps a Callable in a liveness scope, and ensures that when invoked, if that callable
causes the component to render, the scope will be retained by that render pass. It is
Expand All @@ -15,25 +16,30 @@ def use_liveness_scope(func: Callable) -> Callable:

Args:
func: The function to wrap
dependencies: Dependencies of the provided function, so the hook knows when to re-wrap it

Returns:
The wrapped Callable
"""
scope_ref: Ref[LivenessScope | None] = use_ref(None)

# If the value is set, the wrapped callable was invoked since we were last called - the current render
# cycle now will manage it, and release it when appropriate.
if scope_ref.current:
get_context().manage(scope_ref.current)
scope_ref.current = None

def wrapped(*args, **kwargs):
# If one does not exist, create a liveness scope, to hold actions from running the provided callable.
# Instead of releasing it right away, we leave it open until the render queue picks up any changes in
# that callable, see above.
if scope_ref.current is None:
scope_ref.current = LivenessScope()
with scope_ref.current.open():
func(*args, **kwargs)

return wrapped
def make_wrapper():

# If the value is set, the wrapped callable was invoked since we were last called - the current render
# cycle now will manage it, and release it when appropriate.
if scope_ref.current:
get_context().manage(scope_ref.current)
scope_ref.current = None

def wrapped(*args, **kwargs):
# If one does not exist, create a liveness scope, to hold actions from running the provided callable.
# Instead of releasing it right away, we leave it open until the render queue picks up any changes in
# that callable, see above.
if scope_ref.current is None:
scope_ref.current = LivenessScope()
with scope_ref.current.open():
func(*args, **kwargs)

return wrapped

return use_memo(make_wrapper, dependencies)
4 changes: 3 additions & 1 deletion plugins/ui/src/deephaven/ui/hooks/use_table_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ def use_table_data(

table_updated = lambda: _set_new_data(table, sentinel, set_data, set_is_sentinel)

ui.use_table_listener(table, partial(_on_update, ctx, table_updated, executor_name))
ui.use_table_listener(
table, partial(_on_update, ctx, table_updated, executor_name), set()
)

return transform(data, is_sentinel)
9 changes: 7 additions & 2 deletions plugins/ui/src/deephaven/ui/hooks/use_table_listener.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from functools import partial
from typing import Callable
from typing import Any, Callable, Sequence

from deephaven.table import Table
from deephaven.table_listener import listen, TableUpdate, TableListener
Expand Down Expand Up @@ -67,6 +67,7 @@ def wrap_listener(
def use_table_listener(
table: Table,
listener: Callable[[TableUpdate, bool], None] | TableListener,
dependencies: set[Any] | Sequence[Any],
description: str | None = None,
do_replay: bool = False,
replay_lock: LockType = "shared",
Expand All @@ -78,6 +79,7 @@ def use_table_listener(
table: The table to listen to.
listener: Either a function or a TableListener with an on_update function.
The function must take a TableUpdate and is_replay bool.
dependencies: Dependencies of the listener function, so the hook knows when to recreate the listener
description: An optional description for the UpdatePerformanceTracker to append to the listener’s
entry description, default is None.
do_replay: Whether to replay the initial snapshot of the table, default is False.
Expand Down Expand Up @@ -105,4 +107,7 @@ def start_listener() -> Callable[[], None]:

return lambda: handle.stop()

use_effect(start_listener, {table, listener, description, do_replay, replay_lock})
use_effect(
start_listener,
{table, listener, description, do_replay, replay_lock} | set(dependencies),
)
12 changes: 6 additions & 6 deletions plugins/ui/test/deephaven/ui/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_memo(self):
from deephaven.ui.hooks import use_memo

def _test_memo(fn=lambda: "foo", a=1, b=2):
return use_memo(fn, [a, b])
return use_memo(fn, {a, b})

# Initial render
render_result = render_hook(_test_memo)
Expand Down Expand Up @@ -151,7 +151,7 @@ def listener(update: TableUpdate, is_replay: bool) -> None:
event.set()

def _test_table_listener(replayed_table_val=table, listener_val=listener):
use_table_listener(replayed_table_val, listener_val)
use_table_listener(replayed_table_val, listener_val, [])

render_hook(_test_table_listener)

Expand All @@ -171,7 +171,7 @@ def listener(update: TableUpdate, is_replay: bool) -> None:
event.set()

def _test_table_listener(replay_table=table, listener_val=listener):
use_table_listener(replay_table, listener_val, do_replay=True)
use_table_listener(replay_table, listener_val, [], do_replay=True)

render_hook(_test_table_listener)

Expand Down Expand Up @@ -541,7 +541,7 @@ def start_thread():
thread.start()
thread.join()

use_memo(start_thread, [])
use_memo(start_thread, set())

render_hook(_test_execution_context)

Expand All @@ -564,7 +564,7 @@ def start_thread():
thread = threading.Thread(target=thread_func)
thread.start()

use_memo(start_thread, [])
use_memo(start_thread, set())

render_hook(_test_execution_context)

Expand Down Expand Up @@ -595,7 +595,7 @@ def _test_reused_tables():
a, set_a = use_state(lambda: table.where("X=1"))

# When "a" changes, recompute table - don't return or otherwise track this table w.r.t. liveness
replace_a = use_liveness_scope(lambda: set_a(table.where("X=2")))
replace_a = use_liveness_scope(lambda: set_a(table.where("X=2")), set())

return a.size

Expand Down
Loading