Skip to content

Commit 178da20

Browse files
authored
reactive_read() now throws more informative errors (#120)
1 parent 0ea60ef commit 178da20

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ All notable changes to shinywidgets will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [UNRELEASED]
9+
10+
* `reactive_read()` now throws a more informative error when attempting to read non-existing or non-trait attributes. (#120)
11+
812
## [0.2.3] - 2023-11-13
913

1014
* Widgets now `fill` inside of a `fillable` container by default. For examples, see the [ipyleaflet](https://github.com/posit-dev/py-shinywidgets/blob/main/examples/ipyleaflet/app.py), [plotly](https://github.com/posit-dev/py-shinywidgets/blob/main/examples/plotly/app.py), or other [output](https://github.com/posit-dev/py-shinywidgets/blob/main/examples/outputs/app.py) examples. If this intelligent filling isn't desirable, either provide a `height` or `fillable=False` on `output_widget()`. (#115)
11-
* `as_widget()` uses the new `altair.JupyterChart()` to coerce `altair.Chart()` into a `ipywidgets.widgets.Widget` instance. (#120)
15+
* `as_widget()` uses the new `altair.JupyterChart()` to coerce `altair.Chart()` into a `ipywidgets.widgets.Widget` instance. (#113)
1216

1317
## [0.2.2] - 2023-10-31
1418

examples/altair/app.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import altair as alt
2+
from shiny import App, render, ui
3+
from vega_datasets import data
4+
5+
from shinywidgets import output_widget, reactive_read, register_widget
6+
7+
source = data.cars()
8+
9+
app_ui = ui.page_fluid(
10+
ui.output_text_verbatim("selection"),
11+
output_widget("chart")
12+
)
13+
14+
def server(input, output, session):
15+
16+
# Replicate JupyterChart interactivity
17+
# https://altair-viz.github.io/user_guide/jupyter_chart.html#point-selections
18+
brush = alt.selection_point(name="point", encodings=["color"], bind="legend")
19+
chart = alt.Chart(source).mark_point().encode(
20+
x='Horsepower:Q',
21+
y='Miles_per_Gallon:Q',
22+
color=alt.condition(brush, 'Origin:N', alt.value('grey')),
23+
).add_params(brush)
24+
25+
jchart = alt.JupyterChart(chart)
26+
27+
# Display/register the chart in the app_ui
28+
register_widget("chart", jchart)
29+
30+
# Reactive-ly read point selections
31+
@output
32+
@render.text
33+
def selection():
34+
pt = reactive_read(jchart.selections, "point")
35+
return "Selected point: " + str(pt)
36+
37+
app = App(app_ui, server)

shinywidgets/_shinywidgets.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,21 @@ def reactive_depend(
255255
Reactively read a Widget's trait(s)
256256
"""
257257

258-
ctx = reactive.get_current_context() # pyright: ignore[reportPrivateImportUsage]
258+
try:
259+
ctx = reactive.get_current_context() # pyright: ignore[reportPrivateImportUsage]
260+
except RuntimeError:
261+
raise RuntimeError("reactive_read() must be called within a reactive context")
262+
263+
if isinstance(names, str):
264+
names = [names]
265+
266+
for name in names:
267+
if not widget.has_trait(name):
268+
raise ValueError(
269+
f"The '{name}' attribute of {widget.__class__.__name__} is not a "
270+
"widget trait, and so it's not possible to reactively read it. "
271+
"For a list of widget traits, call `.trait_names()` on the widget."
272+
)
259273

260274
def invalidate(change: object):
261275
ctx.invalidate()

0 commit comments

Comments
 (0)