rsdeps

Cargo.lock visualizer (mirror)
Log | Files | Refs | README | LICENSE

commit 853c01f05eb699a038ebd4633ed23b0ca3c68c4d
parent dbac595bd53bb4307f8a337a47b108779ac27ef6
Author: Andy Khramtsov <>
Date:   Sun, 31 May 2026 14:57:27 +0300

refactor: split callback

Diffstat:
Msrc/rsdeps/pages/home.py | 115++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
1 file changed, 70 insertions(+), 45 deletions(-)

diff --git a/src/rsdeps/pages/home.py b/src/rsdeps/pages/home.py @@ -692,15 +692,13 @@ def display_cargo_lock_packages(inputs, state): @callback( dict( - graph=Output(ids.dependency_graph, "figure"), + graph=Output(ids.dependency_graph, "figure", allow_duplicate=True), cache=Output(ids.cache_store, "data"), - selected_node_store=Output(ids.selected_node_store, "data"), - details=Output(ids.selected_node_details, "children"), + selected_node_store=Output(ids.selected_node_store, "data", allow_duplicate=True), + details=Output(ids.selected_node_details, "children", allow_duplicate=True), ), dict( generate=Input(ids.generate_button, "n_clicks"), - click_data=Input(ids.dependency_graph, "clickData"), - reset_highlight_button=Input(ids.reset_highlight_button, "n_clicks"), ), dict( cargo_toml=State(ids.cargo_toml_textarea, "value"), @@ -711,21 +709,73 @@ def display_cargo_lock_packages(inputs, state): ), prevent_initial_call=True, ) -def visualize(inputs, state): - pressed_generate = bool(ctx.triggered_id and ctx.triggered_id == ids.generate_button) - invalid_cache = bool( +def generate(inputs, state): + if not state["cargo_lock"]: + raise PreventUpdate + try: + cargo_toml = parse_cargo_toml(state["cargo_toml"] or "") + except tomllib.TOMLDecodeError: + cargo_toml = None + try: + cargo_lock = parse_cargo_lock(state["cargo_lock"] or "") + except tomllib.TOMLDecodeError as error: + raise PreventUpdate from error + df_graph = files_into_graph(cargo_toml, cargo_lock) + positions = compute_node_positions(df_graph) + adjacency_list = compute_adjacency_list(df_graph) + cache = { + "positions": positions, + "adjacency_list": wrap_adjacency_list(adjacency_list), + "df_graph_nodes": df_graph.nodes.serialize(format="json"), + "df_graph_arcs": df_graph.arcs.serialize(format="json"), + } + + old_fig = state["figure"] + old_uirevision = old_fig.get("layout", {}).get("uirevision", 0) + + fig = draw_graph_figure(df_graph, positions, adjacency_list, selected_node=None) + fig.update_layout( + uirevision=old_uirevision + 1, + ) + + return dict( + graph=fig, + cache=cache, + details="", + selected_node_store=dict(selected=None), + ) + + +@callback( + dict( + graph=Output(ids.dependency_graph, "figure", allow_duplicate=True), + selected_node_store=Output(ids.selected_node_store, "data", allow_duplicate=True), + details=Output(ids.selected_node_details, "children", allow_duplicate=True), + ), + dict( + click_data=Input(ids.dependency_graph, "clickData"), + reset_highlight_button=Input(ids.reset_highlight_button, "n_clicks"), + ), + dict( + cache=State(ids.cache_store, "data"), + selected_node_store=State(ids.selected_node_store, "data"), + figure=State(ids.dependency_graph, "figure"), + ), + prevent_initial_call=True, +) +def update(inputs, state): + if ( not state["cache"] or state["cache"].get("positions") is None or state["cache"].get("df_graph_nodes") is None or state["cache"].get("df_graph_arcs") is None or state["cache"].get("adjacency_list") is None - ) - - clean = pressed_generate or invalid_cache + ): + raise PreventUpdate if ctx.triggered_id and ctx.triggered_id == ids.reset_highlight_button: selected = None - elif inputs["click_data"] and "points" in inputs["click_data"] and not clean: + elif inputs["click_data"] and "points" in inputs["click_data"]: point = inputs["click_data"]["points"][0] selected = point.get("customdata", None) else: @@ -733,34 +783,12 @@ def visualize(inputs, state): if state["selected_node_store"] and state["selected_node_store"].get("selected") == selected: selected = None - if clean: - if not state["cargo_lock"]: - raise PreventUpdate - try: - cargo_toml = parse_cargo_toml(state["cargo_toml"] or "") - except tomllib.TOMLDecodeError: - cargo_toml = None - try: - cargo_lock = parse_cargo_lock(state["cargo_lock"] or "") - except tomllib.TOMLDecodeError as error: - raise PreventUpdate from error - df_graph = files_into_graph(cargo_toml, cargo_lock) - positions = compute_node_positions(df_graph) - adjacency_list = compute_adjacency_list(df_graph) - cache = { - "positions": positions, - "adjacency_list": wrap_adjacency_list(adjacency_list), - "df_graph_nodes": df_graph.nodes.serialize(format="json"), - "df_graph_arcs": df_graph.arcs.serialize(format="json"), - } - else: - positions = state["cache"]["positions"] - adjacency_list = unwrap_adjacency_list(state["cache"]["adjacency_list"]) - df_graph = DfGraph( - nodes=pl.DataFrame.deserialize(io.StringIO(state["cache"]["df_graph_nodes"]), format="json"), - arcs=pl.DataFrame.deserialize(io.StringIO(state["cache"]["df_graph_arcs"]), format="json"), - ) - cache = dash.no_update + positions = state["cache"]["positions"] + adjacency_list = unwrap_adjacency_list(state["cache"]["adjacency_list"]) + df_graph = DfGraph( + nodes=pl.DataFrame.deserialize(io.StringIO(state["cache"]["df_graph_nodes"]), format="json"), + arcs=pl.DataFrame.deserialize(io.StringIO(state["cache"]["df_graph_arcs"]), format="json"), + ) details = [ item @@ -773,16 +801,13 @@ def visualize(inputs, state): ][:-1] old_fig = state["figure"] - old_uirevision = old_fig.get("layout", {}).get("uirevision", 0) + uirevision = old_fig.get("layout", {}).get("uirevision", 0) fig = draw_graph_figure(df_graph, positions, adjacency_list, selected) - fig.update_layout( - uirevision=old_uirevision + 1 if clean else old_uirevision, - ) + fig.update_layout(uirevision=uirevision) return dict( graph=fig, - cache=cache, details=details, selected_node_store=dict(selected=selected), )