Skip to content

Commit 03caffe

Browse files
authored
Merge pull request #79 from rstudio/value-is-set
2 parents e9c73d9 + dcac4af commit 03caffe

File tree

2 files changed

+43
-16
lines changed

2 files changed

+43
-16
lines changed

shiny/reactive/_reactives.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@ def __init__(
5252
) -> None:
5353
self._value: T = value
5454
self._read_only: bool = read_only
55-
self._dependents: Dependents = Dependents()
55+
self._value_dependents: Dependents = Dependents()
56+
self._is_set_dependents: Dependents = Dependents()
5657

5758
def __call__(self) -> T:
5859
return self.get()
5960

6061
def get(self) -> T:
61-
self._dependents.register()
62+
self._value_dependents.register()
6263

6364
if isinstance(self._value, MISSING_TYPE):
6465
raise SilentException
@@ -78,15 +79,18 @@ def _set(self, value: T) -> bool:
7879
if self._value is value:
7980
return False
8081

82+
if isinstance(self._value, MISSING_TYPE) != isinstance(value, MISSING_TYPE):
83+
self._is_set_dependents.invalidate()
84+
8185
self._value = value
82-
self._dependents.invalidate()
86+
self._value_dependents.invalidate()
8387
return True
8488

8589
def unset(self) -> None:
8690
self.set(MISSING) # type: ignore
8791

8892
def is_set(self) -> bool:
89-
self._dependents.register()
93+
self._is_set_dependents.register()
9094
return not isinstance(self._value, MISSING_TYPE)
9195

9296
# Like unset(), except that this does not invalidate dependents.

tests/test_reactives.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from shiny.reactive._core import ReactiveWarning
99
from shiny._decorators import *
1010
from shiny.reactive import *
11-
from shiny.types import MISSING
1211
from shiny._validation import SilentException, req
1312

1413
from .mocktime import MockTime
@@ -96,7 +95,7 @@ def o():
9695
# ======================================================================
9796
@pytest.mark.asyncio
9897
async def test_reactive_value_unset():
99-
v = Value[int](MISSING)
98+
v = Value[int]()
10099

101100
with isolate():
102101
assert v.is_set() is False
@@ -118,36 +117,60 @@ def o():
118117
await flush()
119118
assert o._exec_count == 2
120119
assert val == 1
120+
with isolate():
121+
assert v.is_set() is True
121122

122123
v.unset()
123124
await flush()
124125
assert o._exec_count == 3
125126
assert val == 1
126-
127127
with isolate():
128128
assert v.is_set() is False
129129
with pytest.raises(SilentException):
130130
v()
131131

132-
# Check that dependency is taken when is_set() is called.
133-
v = Value[int](MISSING)
134-
val2: Union[bool, None] = None
132+
133+
# ======================================================================
134+
# reactive.Value.is_set() invalidates dependents only when set state changes
135+
# ======================================================================
136+
@pytest.mark.asyncio
137+
async def test_reactive_value_is_set():
138+
v = Value[int]()
139+
v_is_set: bool = False
135140

136141
@Effect()
137-
def o2():
138-
nonlocal val2
139-
val2 = v.is_set()
142+
def o():
143+
nonlocal v_is_set
144+
v_is_set = v.is_set()
140145

141146
await flush()
142-
assert val2 is False
147+
assert o._exec_count == 1
148+
assert v_is_set is False
143149

144150
v.set(1)
145151
await flush()
146-
assert val2 is True
152+
assert o._exec_count == 2
153+
assert v_is_set is True
154+
155+
v.set(2)
156+
await flush()
157+
assert o._exec_count == 2
158+
assert v_is_set is True
147159

148160
v.unset()
149161
await flush()
150-
assert val2 is False
162+
assert o._exec_count == 3
163+
assert v_is_set is False
164+
165+
v.unset()
166+
await flush()
167+
assert o._exec_count == 3
168+
assert v_is_set is False
169+
170+
v.set(1)
171+
await flush()
172+
assert o._exec_count == 4
173+
assert v_is_set is True
151174

152175

153176
# ======================================================================

0 commit comments

Comments
 (0)