Skip to content

Commit 6fc7797

Browse files
authored
Merge pull request ipython#393 from francisco-dlp/link_tranform
Link tranform
2 parents 7a71282 + f8cde3b commit 6fc7797

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

traitlets/tests/test_traitlets.py

+50
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,56 @@ def b_callback(name, old, new):
18981898
self.assertEqual(''.join(callback_count), 'ab')
18991899
del callback_count[:]
19001900

1901+
def test_tranform(self):
1902+
"""Test transform link."""
1903+
1904+
# Create two simple classes with Int traitlets.
1905+
class A(HasTraits):
1906+
value = Int()
1907+
a = A(value=9)
1908+
b = A(value=8)
1909+
1910+
# Conenct the two classes.
1911+
c = link((a, 'value'), (b, 'value'),
1912+
transform=(lambda x: 2 * x, lambda x: int(x / 2.)))
1913+
1914+
# Make sure the values are correct at the point of linking.
1915+
self.assertEqual(b.value, 2 * a.value)
1916+
1917+
# Change one the value of the source and check that it modifies the target.
1918+
a.value = 5
1919+
self.assertEqual(b.value, 10)
1920+
# Change one the value of the target and check that it modifies the
1921+
# source.
1922+
b.value = 6
1923+
self.assertEqual(a.value, 3)
1924+
1925+
def test_link_broken_at_source(self):
1926+
class MyClass(HasTraits):
1927+
i = Int()
1928+
j = Int()
1929+
1930+
@observe("j")
1931+
def another_update(self, change):
1932+
self.i = change.new * 2
1933+
1934+
mc = MyClass()
1935+
l = link((mc, "i"), (mc, "j"))
1936+
self.assertRaises(TraitError, setattr, mc, 'i', 2)
1937+
1938+
def test_link_broken_at_target(self):
1939+
class MyClass(HasTraits):
1940+
i =Int()
1941+
j = Int()
1942+
1943+
@observe("i")
1944+
def another_update(self, change):
1945+
self.j = change.new * 2
1946+
1947+
mc = MyClass()
1948+
l = link((mc, "i"), (mc, "j"))
1949+
self.assertRaises(TraitError, setattr, mc, 'j', 2)
1950+
19011951
class TestDirectionalLink(TestCase):
19021952
def test_connect_same(self):
19031953
"""Verify two traitlets of the same type can be linked together using directional_link."""

traitlets/traitlets.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ class link(object):
251251
----------
252252
source : (object / attribute name) pair
253253
target : (object / attribute name) pair
254+
transform: iterable with two callables (optional)
255+
Data transformation between source and target and target and source.
254256
255257
Examples
256258
--------
@@ -260,15 +262,19 @@ class link(object):
260262
"""
261263
updating = False
262264

263-
def __init__(self, source, target):
265+
def __init__(self, source, target, transform=None):
264266
_validate_link(source, target)
265267
self.source, self.target = source, target
268+
self._transform, self._transform_inv = (
269+
transform if transform else (lambda x: x,) * 2)
270+
266271
self.link()
267272

268273
def link(self):
269274
try:
270275
setattr(self.target[0], self.target[1],
271-
getattr(self.source[0], self.source[1]))
276+
self._transform(getattr(self.source[0], self.source[1])))
277+
272278
finally:
273279
self.source[0].observe(self._update_target, names=self.source[1])
274280
self.target[0].observe(self._update_source, names=self.target[1])
@@ -285,13 +291,22 @@ def _update_target(self, change):
285291
if self.updating:
286292
return
287293
with self._busy_updating():
288-
setattr(self.target[0], self.target[1], change.new)
294+
setattr(self.target[0], self.target[1], self._transform(change.new))
295+
if getattr(self.source[0], self.source[1]) != change.new:
296+
raise TraitError(
297+
"Broken link {}: the source value changed while updating "
298+
"the target.".format(self))
289299

290300
def _update_source(self, change):
291301
if self.updating:
292302
return
293303
with self._busy_updating():
294-
setattr(self.source[0], self.source[1], change.new)
304+
setattr(self.source[0], self.source[1],
305+
self._transform_inv(change.new))
306+
if getattr(self.target[0], self.target[1]) != change.new:
307+
raise TraitError(
308+
"Broken link {}: the target value changed while updating "
309+
"the source.".format(self))
295310

296311
def unlink(self):
297312
self.source[0].unobserve(self._update_target, names=self.source[1])

0 commit comments

Comments
 (0)