anywidget
anywidget copied to clipboard
VS Code: Failed to load model class 'AnyModel' from module 'anywidget'
Describe the bug
First of all, thanks for this awesome package! It has made developing a widget for my package (https://github.com/pipefunc/pipefunc/pull/323) much easier!
My widget works in a Jupyter notebook but in VS Code I get:
[Open Browser Console for more detailed log - Double click to close this message]
Failed to load model class 'AnyModel' from module 'anywidget'
Error: No version of module anywidget is registered
at ph.loadClass (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4099813)
at ph.loadClass (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4403287)
at ph.loadModelClass (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4097773)
at ph._make_model (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4094616)
at ph.new_model (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4092246)
at ph.handle_comm_open (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4091039)
at https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-renderers-1.0.19/out/node_modules/%40vscode/jupyter-ipywidgets8/dist/ipywidgets.js:2:4402511
at n._handleCommOpen (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-2024.8.1-darwin-arm64/dist/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js:3:80955)
at async n._handleMessage (https://file+.vscode-resource.vscode-cdn.net/Users/basnijholt/.vscode/extensions/ms-toolsai.jupyter-2024.8.1-darwin-arm64/dist/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js:3:82830)
I found some related issue here:
- https://github.com/vega/altair/issues/3409#issuecomment-2075544491
- https://github.com/manzt/anywidget/issues/534
Reproduction
I have this widget
from pathlib import Path
from typing import Any
import anywidget
import ipywidgets as widgets
import traitlets
class PipeFuncGraphWidget(anywidget.AnyWidget):
"""A widget for rendering a graphviz graph using d3-graphviz.
Example:
-------
>>> dot_string = "digraph { a -> b; b -> c; c -> a; }"
>>> pipe_func_graph_widget = PipeFuncGraphWidget(dot_source=dot_string)
>>> pipe_func_graph_widget
"""
_esm = Path(__file__).parent / "static" / "graphviz_widget.js"
_css = """
#graph {
margin: auto;
}
"""
dot_source = traitlets.Unicode("").tag(sync=True)
selected_direction = traitlets.Unicode("bidirectional").tag(sync=True)
def graph_widget(dot_string: str = "digraph { a -> b; b -> c; c -> a; }") -> widgets.VBox:
pipe_func_graph_widget = PipeFuncGraphWidget(dot_source=dot_string)
reset_button = widgets.Button(description="Reset Zoom")
direction_selector = widgets.Dropdown(
options=["bidirectional", "downstream", "upstream", "single"],
value="bidirectional",
description="Direction:",
)
# Define button actions
def reset_graph(_: Any) -> None:
pipe_func_graph_widget.send({"action": "reset_zoom"})
def update_direction(change: dict) -> None:
pipe_func_graph_widget.selected_direction = change["new"]
reset_button.on_click(reset_graph)
direction_selector.observe(update_direction, names="value")
# Display widgets
return widgets.VBox(
[
widgets.HBox([reset_button, direction_selector]),
pipe_func_graph_widget,
],
)
with graphviz_widget.js:
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = url;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load script: ${url}`));
document.head.append(script);
});
}
async function render({ model, el }) {
// Load scripts in order
await loadScript("https://unpkg.com/[email protected]/dist/jquery.min.js");
await loadScript("https://unpkg.com/[email protected]/jquery.mousewheel.js");
await loadScript("https://unpkg.com/[email protected]/dist/jquery.color.js");
await loadScript("https://unpkg.com/[email protected]/dist/d3.min.js");
await loadScript("https://cdn.jsdelivr.net/gh/mountainstorm/jquery.graphviz.svg@master/js/jquery.graphviz.svg.js");
await loadScript("https://unpkg.com/@hpcc-js/[email protected]/dist/index.min.js");
await loadScript("https://unpkg.com/[email protected]/build/d3-graphviz.min.js");
const $ = window.jQuery;
const d3 = window.d3;
// Prepare the graph container
el.innerHTML = '<div id="graph" style="text-align: center;"></div>';
const graphEl = $(el).find('#graph');
// Initialize a d3-graphviz renderer instance
var graphviz = d3.select("#graph").graphviz();
// Configuration for transitions in rendering the graph
var d3Config = {
transitionDelay: 0,
transitionDuration: 500
};
// Variable for storing the selected graph rendering engine
var selectedEngine = "dot";
// Object for saving the current GraphVizSVG
var graphVizObject;
// Variable for storing the selected direction for highlighting
var selectedDirection = model.get("selected_direction") || "bidirectional";
// Array holding the current selections
var currentSelection = [];
// Function to highlight selected nodes and their connected nodes
function highlightSelection() {
let highlightedNodes = $();
currentSelection.forEach(selection => {
const nodes = getConnectedNodes(selection.set, selection.direction);
highlightedNodes = highlightedNodes.add(nodes);
});
graphVizObject.highlight(highlightedNodes, true);
}
// Function to retrieve nodes connected in the specified direction
function getConnectedNodes(nodeSet, mode = "bidirectional") {
let resultSet = $().add(nodeSet);
const nodes = graphVizObject.nodesByName();
nodeSet.each((i, el) => {
if (el.className.baseVal === "edge") {
const [startNode, endNode] = $(el).data("name").split("->");
if ((mode === "bidirectional" || mode === "upstream") && startNode) {
resultSet = resultSet.add(nodes[startNode]).add(graphVizObject.linkedTo(nodes[startNode], true));
}
if ((mode === "bidirectional" || mode === "downstream") && endNode) {
resultSet = resultSet.add(nodes[endNode]).add(graphVizObject.linkedFrom(nodes[endNode], true));
}
} else {
if (mode === "bidirectional" || mode === "upstream") {
resultSet = resultSet.add(graphVizObject.linkedTo(el, true));
}
if (mode === "bidirectional" || mode === "downstream") {
resultSet = resultSet.add(graphVizObject.linkedFrom(el, true));
}
}
});
return resultSet;
}
// Function to reset the graph zoom and selection highlights
function resetGraph() {
graphviz.resetZoom();
graphVizObject.highlight(); // Reset node selection on reset
currentSelection = [];
}
// Function to update the selected direction for highlighting
function updateDirection(newDirection) {
selectedDirection = newDirection;
resetGraph();
}
// Main function to render the graph from DOT source
function render(dotSource) {
var transition = d3.transition("graphTransition")
.ease(d3.easeLinear)
.delay(d3Config.transitionDelay)
.duration(d3Config.transitionDuration);
graphviz
.engine(selectedEngine)
.fade(true)
.transition(transition)
.tweenPaths(true)
.tweenShapes(true)
.zoomScaleExtent([0, Infinity])
.zoom(true)
.renderDot(dotSource)
.on("end", function () {
// Calls the jquery.graphviz.svg setup directly
$('#graph').data('graphviz.svg').setup(); // Re-setup after rendering
});
}
// Document ready function
$(document).ready(function () {
// Initialize the GraphVizSVG object from jquery.graphviz.svg.js
$("#graph").graphviz({
shrink: null,
zoom: false,
ready: function () {
graphVizObject = this;
// Event listener for node clicks to handle selection
graphVizObject.nodes().click(function (event) {
const nodeSet = $().add(this);
const selectionObject = {
set: nodeSet,
direction: selectedDirection
};
// If CMD, CTRL, or SHIFT is pressed, add to the selection
if (event.ctrlKey || event.metaKey || event.shiftKey) {
currentSelection.push(selectionObject);
} else {
currentSelection = [selectionObject];
}
highlightSelection();
});
// Event listener for pressing the escape key to cancel highlights
$(document).keydown(function (event) {
if (event.keyCode === 27) {
graphVizObject.highlight();
}
});
}
});
});
// Event listeners for `anywidget` events
model.on("change:dot_source", () => {
render(model.get("dot_source"));
});
model.on("change:selected_direction", () => {
updateDirection(model.get("selected_direction"));
});
model.on("msg:custom", (msg) => {
if (msg.action === "reset_zoom") {
resetGraph();
}
});
render(model.get("dot_source"));
}
export default { render };
The I run:
graph_widget()
In a Jupyter notebook in the browser this works, but in VS Code I see the error I posted above.
Logs
No response
System Info
at 16:44:43 ❯ pip freeze | grep -E "anywidget|jupyter|notebook"
anywidget @ file:///home/conda/feedstock_root/build_artifacts/anywidget_1719028522224/work
jupyter-cache==1.0.0
jupyter-events @ file:///home/conda/feedstock_root/build_artifacts/jupyter_events_1710805637316/work
jupyter-lsp @ file:///home/conda/feedstock_root/build_artifacts/jupyter-lsp-meta_1712707420468/work/jupyter-lsp
jupyter_client @ file:///home/conda/feedstock_root/build_artifacts/jupyter_client_1716472197302/work
jupyter_core @ file:///Users/runner/miniforge3/conda-bld/jupyter_core_1710257589360/work
jupyter_server @ file:///home/conda/feedstock_root/build_artifacts/jupyter_server_1720816649297/work
jupyter_server_terminals @ file:///home/conda/feedstock_root/build_artifacts/jupyter_server_terminals_1710262634903/work
jupyterlab @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_1724745148804/work
jupyterlab_pygments @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_pygments_1707149102966/work
jupyterlab_server @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_server-split_1721163288448/work
jupyterlab_widgets @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_widgets_1724331334887/work
notebook @ file:///home/conda/feedstock_root/build_artifacts/notebook_1724861303656/work
notebook_shim @ file:///home/conda/feedstock_root/build_artifacts/notebook-shim_1707957777232/work
### Severity
blocking all usage of anywidget