Skip to content

Commit f2b6b9b

Browse files
authored
Merge pull request #21 from FancyNeuron/18-make-variablebind-use-new-weak_observe-method
Make variables bind weakly
2 parents 11cd96d + 9c399cf commit f2b6b9b

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

src/pybind/values.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def value(self, new_value: _S) -> None:
7171
raise NotImplementedError
7272

7373
@abstractmethod
74-
def bind_to(self, value: Value[_S], already_bound_ok: bool = False) -> None:
74+
def bind_to(self, value: Value[_S], already_bound_ok: bool = False, bind_weakly: bool = True) -> None:
7575
raise NotImplementedError
7676

7777
@abstractmethod
@@ -117,7 +117,7 @@ def weak_observe(self, observer: Observer | ValueObserver[_S]) -> None:
117117
def unobserve(self, observer: Observer | ValueObserver[_S]) -> None:
118118
self._on_change.unobserve(observer)
119119

120-
def bind_to(self, value: Value[_S], already_bound_ok: bool = False) -> None:
120+
def bind_to(self, value: Value[_S], already_bound_ok: bool = False, bind_weakly: bool = True) -> None:
121121
if value is self:
122122
raise RecursionError("Cannot bind a Variable to itself.")
123123
if value.is_dependent_on(self):
@@ -128,7 +128,10 @@ def bind_to(self, value: Value[_S], already_bound_ok: bool = False) -> None:
128128
if self._bound_to is value:
129129
return
130130
self.unbind()
131-
value.observe(self._receive_bound_value)
131+
if bind_weakly:
132+
value.weak_observe(self._receive_bound_value)
133+
else:
134+
value.observe(self._receive_bound_value)
132135
self._bound_to = value
133136
self._bound_to_set = frozenset([value])
134137
self._set_value_bypass_bound_check(value.value)

tests/test_simple_variable.py

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import gc
2+
13
import pytest
24
from pybind.values import SimpleVariable, Constant
35
from conftest import NoParametersObserver, OneParameterObserver
@@ -313,13 +315,52 @@ def test_simple_variable_deep_derived_from_diamond_pattern():
313315
assert variable_top in dependencies
314316

315317

316-
def test_constant_derived_from_empty():
317-
constant = Constant("test")
318+
def test_bind_weak_reference_clears():
319+
root_var = SimpleVariable("root0")
320+
dependent_var = SimpleVariable("")
321+
dependent_var.bind_to(root_var, bind_weakly=True)
322+
323+
values = []
324+
dependent_var.observe(lambda value: values.append(value))
325+
326+
root_var.value = "root1"
327+
dependent_var = None
328+
gc.collect()
329+
root_var.value = "root2"
330+
331+
assert values == ["root1"]
332+
333+
334+
def test_bind_strong_reference_stays():
335+
root_var = SimpleVariable("root0")
336+
dependent_var = SimpleVariable("")
337+
dependent_var.bind_to(root_var, bind_weakly=False)
338+
339+
values = []
340+
dependent_var.observe(lambda value: values.append(value))
341+
342+
root_var.value = "root1"
343+
dependent_var = None
344+
gc.collect()
345+
root_var.value = "root2"
346+
347+
assert values == ["root1", "root2"]
348+
349+
350+
def test_daisy_chain_variables_weak_reference_stays():
351+
root_var = SimpleVariable("root0")
352+
middle_var = SimpleVariable("")
353+
dependent_var = SimpleVariable("")
318354

319-
assert constant.derived_from() == frozenset()
355+
middle_var.bind_to(root_var, bind_weakly=True)
356+
dependent_var.bind_to(middle_var, bind_weakly=False)
320357

358+
values = []
359+
dependent_var.observe(lambda value: values.append(value))
321360

322-
def test_constant_deep_derived_from_empty():
323-
constant = Constant("test")
361+
root_var.value = "root1"
362+
middle_var = None
363+
gc.collect()
364+
root_var.value = "root2"
324365

325-
assert list(constant.deep_derived_from) == []
366+
assert values == ["root1", "root2"]

0 commit comments

Comments
 (0)