diff --git a/docker/data/storage/notebooks/DEMO.md b/docker/data/storage/notebooks/DEMO.md index 9db7ef902..19fff5579 100644 --- a/docker/data/storage/notebooks/DEMO.md +++ b/docker/data/storage/notebooks/DEMO.md @@ -97,7 +97,7 @@ def picker(): ) -p = picker() +result = picker() ``` ## Using Tables @@ -197,7 +197,7 @@ def stock_table_input(source, default_sym="", default_exchange=""): return [ ui.panel( # Add a callback for when user double clicks a row in the table - ui.table(t1).on_row_double_press(handle_row_double_press), + ui.table(t1, on_row_double_press=handle_row_double_press), title="Stock Row Press", ), ui.panel(t2, title="Stock Filtered Table"), @@ -226,7 +226,7 @@ def hist_demo(source, column): x=column, nbins=bin_count, ), - {bin_count, hist_range, source, column}, + [bin_count, hist_range, source, column], ) return [ @@ -271,7 +271,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( @@ -322,7 +322,7 @@ def table_tabs(source): return ui.tabs( ui.tab_list( ui.item("Unfiltered", key="Unfiltered"), - ui.item(ui.icon("vsGithubAlt"), "CAT", key="CAT"), + ui.item(ui.icon("vsGithubAlt"), ui.text("CAT"), key="CAT"), ui.item("DOG", key="DOG"), ), ui.tab_panels( @@ -437,18 +437,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( diff --git a/package.json b/package.json index e656220ed..dacf6f0b5 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test:ci:unit": "jest --config jest.config.unit.cjs --ci --cacheDirectory $PWD/.jest-cache", "test:ci:lint": "jest --config jest.config.lint.cjs --ci --cacheDirectory $PWD/.jest-cache", "e2e": "playwright run", - "e2e:docker": "./tools/run_docker.sh e2e-tests", + "e2e:docker": "DEEPHAVEN_PORT=10001 ./tools/run_docker.sh e2e-tests", "e2e:update-snapshots": "./tools/run_docker.sh update-snapshots", "update-dh-packages": "lerna run update-dh-packages" }, diff --git a/plugins/ui/examples/README.md b/plugins/ui/examples/README.md index d464639f4..958477489 100644 --- a/plugins/ui/examples/README.md +++ b/plugins/ui/examples/README.md @@ -758,7 +758,7 @@ def stock_table_input(source, default_sym="", default_exchange=""): return [ ui.panel( - ui.table(t1).on_row_double_press(handle_row_double_press), + ui.table(t1, on_row_double_press=handle_row_double_press), title="Stock Table Input", ), ui.panel(t2, title="Stock Filtered Table"), diff --git a/plugins/ui/src/deephaven/ui/hooks/use_callback.py b/plugins/ui/src/deephaven/ui/hooks/use_callback.py index 7496e1205..a5fedf436 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_callback.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_callback.py @@ -3,9 +3,10 @@ from typing import Callable, Any, Sequence from .use_ref import use_ref, Ref +from ..types import Dependencies -def use_callback(func: Callable, dependencies: set[Any] | Sequence[Any]) -> Callable: +def use_callback(func: Callable, dependencies: Dependencies) -> Callable: """ Create a stable handle for a callback function. The callback will only be recreated if the dependencies change. diff --git a/plugins/ui/src/deephaven/ui/hooks/use_effect.py b/plugins/ui/src/deephaven/ui/hooks/use_effect.py index 713c3324e..e11e05fa6 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_effect.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_effect.py @@ -4,9 +4,10 @@ from .use_ref import use_ref, Ref from deephaven.liveness_scope import LivenessScope from .._internal import get_context +from ..types import Dependencies -def use_effect(func: Callable[[], Any], dependencies: set[Any] | Sequence[Any]): +def use_effect(func: Callable[[], Any], dependencies: Dependencies): """ Call a function when the dependencies change. Optionally return a cleanup function to be called when dependencies change again or component is unmounted. diff --git a/plugins/ui/src/deephaven/ui/hooks/use_execution_context.py b/plugins/ui/src/deephaven/ui/hooks/use_execution_context.py index 93f1c9045..2cfc808a1 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_execution_context.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_execution_context.py @@ -35,6 +35,6 @@ def use_execution_context( Returns: A callable that will take any callable and invoke it within the current exec context """ - exec_ctx = use_memo(lambda: exec_ctx if exec_ctx else get_exec_ctx(), {exec_ctx}) - exec_fn = use_memo(lambda: partial(func_with_ctx, exec_ctx), {exec_ctx}) + exec_ctx = use_memo(lambda: exec_ctx if exec_ctx else get_exec_ctx(), [exec_ctx]) + exec_fn = use_memo(lambda: partial(func_with_ctx, exec_ctx), [exec_ctx]) return exec_fn diff --git a/plugins/ui/src/deephaven/ui/hooks/use_liveness_scope.py b/plugins/ui/src/deephaven/ui/hooks/use_liveness_scope.py index 1cce94bdd..87acd3958 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_liveness_scope.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_liveness_scope.py @@ -5,9 +5,10 @@ from .use_ref import use_ref, Ref from typing import Callable from deephaven.liveness_scope import LivenessScope +from ..types import Dependencies -def use_liveness_scope(func: Callable, dependencies: set) -> Callable: +def use_liveness_scope(func: Callable, dependencies: Dependencies) -> 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 diff --git a/plugins/ui/src/deephaven/ui/hooks/use_memo.py b/plugins/ui/src/deephaven/ui/hooks/use_memo.py index 39b384449..406714539 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_memo.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_memo.py @@ -4,11 +4,12 @@ from .._internal import ValueWithLiveness, get_context from typing import Any, Callable, TypeVar, cast, Union, Sequence from deephaven.liveness_scope import LivenessScope +from ..types import Dependencies T = TypeVar("T") -def use_memo(func: Callable[[], T], dependencies: set[Any] | Sequence[Any]) -> T: +def use_memo(func: Callable[[], T], dependencies: Dependencies) -> T: """ Memoize the result of a function call. The function will only be called again if the dependencies change. @@ -19,6 +20,11 @@ def use_memo(func: Callable[[], T], dependencies: set[Any] | Sequence[Any]) -> T Returns: The memoized result of the function call. """ + if not isinstance(dependencies, (list, tuple)): + raise TypeError( + f"dependencies must be a list or tuple, got {type(dependencies)}" + ) + deps_ref: Ref[set[Any] | Sequence[Any] | None] = use_ref(None) value_ref: Ref[ValueWithLiveness[T | None]] = use_ref( ValueWithLiveness(value=cast(Union[T, None], None), liveness_scope=None) diff --git a/plugins/ui/src/deephaven/ui/hooks/use_table_data.py b/plugins/ui/src/deephaven/ui/hooks/use_table_data.py index 985002e3f..3f2714612 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_table_data.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_table_data.py @@ -143,7 +143,7 @@ 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), set() + table, partial(_on_update, ctx, table_updated, executor_name), [] ) return transform(data, is_sentinel) diff --git a/plugins/ui/src/deephaven/ui/hooks/use_table_listener.py b/plugins/ui/src/deephaven/ui/hooks/use_table_listener.py index da19b87a4..a7eef4ad5 100644 --- a/plugins/ui/src/deephaven/ui/hooks/use_table_listener.py +++ b/plugins/ui/src/deephaven/ui/hooks/use_table_listener.py @@ -8,7 +8,7 @@ from deephaven.execution_context import get_exec_ctx, ExecutionContext from .use_effect import use_effect -from ..types import LockType +from ..types import LockType, Dependencies def listener_with_ctx( @@ -67,7 +67,7 @@ def wrap_listener( def use_table_listener( table: Table, listener: Callable[[TableUpdate, bool], None] | TableListener, - dependencies: set[Any] | Sequence[Any], + dependencies: Dependencies, description: str | None = None, do_replay: bool = False, replay_lock: LockType = "shared", @@ -109,5 +109,5 @@ def start_listener() -> Callable[[], None]: use_effect( start_listener, - {table, listener, description, do_replay, replay_lock} | set(dependencies), + [table, listener, description, do_replay, replay_lock] + list(dependencies), ) diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py index ac84792bf..7962f207d 100644 --- a/plugins/ui/src/deephaven/ui/types/types.py +++ b/plugins/ui/src/deephaven/ui/types/types.py @@ -105,3 +105,4 @@ class RowDataValue(CellData): # Stringable is a type that is naturally convertible to a string Stringable = Union[str, int, float, bool] Key = Stringable +Dependencies = Union[Tuple[Any], List[Any]] diff --git a/plugins/ui/test/deephaven/ui/test_hooks.py b/plugins/ui/test/deephaven/ui/test_hooks.py index 1a9fcfb8c..dce874bb1 100644 --- a/plugins/ui/test/deephaven/ui/test_hooks.py +++ b/plugins/ui/test/deephaven/ui/test_hooks.py @@ -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) @@ -140,6 +140,12 @@ def _test_memo(fn=lambda: "foo", a=1, b=2): self.assertEqual(result, "biz") self.assertEqual(mock.call_count, 1) + def _test_memo_set(fn=lambda: "foo"): + return use_memo(fn, {}) + + # passing in a non-list/tuple for dependencies should raise a TypeError + self.assertRaises(TypeError, render_hook, _test_memo_set) + def verify_table_updated(self, table_writer, table, update): from deephaven.ui.hooks import use_table_listener from deephaven.table_listener import TableUpdate @@ -541,7 +547,7 @@ def start_thread(): thread.start() thread.join() - use_memo(start_thread, set()) + use_memo(start_thread, []) render_hook(_test_execution_context) @@ -564,7 +570,7 @@ def start_thread(): thread = threading.Thread(target=thread_func) thread.start() - use_memo(start_thread, set()) + use_memo(start_thread, []) render_hook(_test_execution_context) @@ -595,7 +601,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")), set()) + replace_a = use_liveness_scope(lambda: set_a(table.where("X=2")), []) return a.size @@ -664,7 +670,7 @@ def helper(): now = dh_now() return table.where("Timestamp > now").last_by(by=["X"]) - local_rows = use_memo(helper, {a}) + local_rows = use_memo(helper, [a]) return local_rows.size diff --git a/tools/run_docker.sh b/tools/run_docker.sh index 6d37d8f8f..649c3b911 100755 --- a/tools/run_docker.sh +++ b/tools/run_docker.sh @@ -4,7 +4,11 @@ pushd "$(dirname "$0")" # Start the containers -docker compose run --service-ports --rm --build "$@" +if [[ -z "${CI}" ]]; then + docker compose run --service-ports --rm --build "$@" +else + docker compose run --service-ports --rm --build -e CI=true "$@" +fi exit_code=$? docker compose down