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

Allow for the model_id property to still be accessed after the widget is closed #179

Merged
merged 2 commits into from
Jan 23, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED] - 2025-01-23

* Fixed an issue with plotly graphs sometimes not getting fully removed from the DOM. (#178)
* Fixed an issue with ipyleaflet erroring out when attempting to read the `.model_id` property of a closed widget object. (#179)

## [0.4.2] - 2024-12-18

Expand Down
28 changes: 28 additions & 0 deletions shinywidgets/_comm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from base64 import b64encode
from dataclasses import dataclass
from typing import Callable, Dict, List, Optional

from shiny._utils import run_coro_hybrid
Expand Down Expand Up @@ -196,3 +197,30 @@ def handle_msg(self, msg: Dict[str, object]) -> None:
def handle_close(self, msg: Dict[str, object]) -> None:
if self._close_callback is not None:
self._close_callback(msg)


@dataclass
class OrphanedShinyComm:
"""
A 'mock' `ShinyComm`. It's only purpose is to allow one to get
the `model_id` (i.e., `comm_id`) of a widget after closing it.
"""

comm_id: str

def send(
self,
*args: object,
**kwargs: object,
) -> None:
pass

def close(
self,
*args: object,
**kwargs: object,
) -> None:
pass

def on_msg(self, callback: MsgCallback) -> None:
pass
13 changes: 11 additions & 2 deletions shinywidgets/_shinywidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from ._as_widget import as_widget
from ._cdn import SHINYWIDGETS_CDN_ONLY, SHINYWIDGETS_EXTENSION_WARNING
from ._comm import BufferType, ShinyComm, ShinyCommManager
from ._comm import BufferType, OrphanedShinyComm, ShinyComm, ShinyCommManager
from ._dependencies import require_dependency
from ._render_widget_base import has_current_context
from ._utils import package_dir
Expand Down Expand Up @@ -134,7 +134,16 @@ def _cleanup_session_state():
def on_close():
with session_context(session):
w.close()

# By closing the widget, we also close the comm, which sets w.comm to
# None. Unfortunately, the w.model_id property looks up w.comm.comm_id
# at runtime, and some packages like ipyleaflet want to use this id
# to manage references between widgets.
w.comm = OrphanedShinyComm(id)
# Assigning a comm has the side-effect of adding back a reference to
# the widget instance, so remove it again
if id in WIDGET_INSTANCE_MAP:
del WIDGET_INSTANCE_MAP[id]

ctx.on_invalidate(on_close)

# Keep track of what session this widget belongs to (so we can close it when the
Expand Down
Loading