From fda166627b6c97db8998fa50586c1364b95b0920 Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Thu, 12 Jul 2018 15:39:13 +0200 Subject: [PATCH 1/7] Update AliasProperty caching to cache value only if "cache" argument is True. Fixes the case when "bind" is None or empty list/tuple and "cache" is True. If "force_dispatch" is True, it will always dispatch current value on each setter call. --- kivy/properties.pyx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index ca7522d951..42687cefc5 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -1404,8 +1404,10 @@ cdef class AliasProperty(Property): `bind`: list/tuple Properties to observe for changes, as property name strings `cache`: boolean - If True, the value will be cached, until one of the binded elements - will changes + If True, the value will be cached, until one of the binded + elements changes. If `bind` collection is empty and `cache` is + True, `setter` must return True in order to cache new value and + trigger its dispatch. `rebind`: bool, defaults to False See :class:`ObjectProperty` for details. @@ -1448,10 +1450,11 @@ cdef class AliasProperty(Property): cpdef trigger_change(self, EventDispatcher obj, value): cdef PropertyStorage ps = obj.__storage[self._name] - ps.alias_initial = 0 dvalue = ps.getter(obj) if ps.value != dvalue: - ps.value = dvalue + if self.use_cache: + ps.alias_initial = 0 + ps.value = dvalue self.dispatch(obj) cdef check(self, EventDispatcher obj, value): @@ -1461,14 +1464,20 @@ cdef class AliasProperty(Property): cdef PropertyStorage ps = obj.__storage[self._name] if self.use_cache: if ps.alias_initial: - return ps.getter(obj) + ps.alias_initial = 0 + ps.value = ps.getter(obj) return ps.value return ps.getter(obj) cpdef set(self, EventDispatcher obj, value): cdef PropertyStorage ps = obj.__storage[self._name] if ps.setter(obj, value): - ps.value = self.get(obj) + if self.use_cache: + if ps.alias_initial: + ps.alias_initial = 0 + ps.value = ps.getter(obj) + self.dispatch(obj) + elif self.force_dispatch: self.dispatch(obj) cpdef dispatch(self, EventDispatcher obj): From 4167cf076d83a19081f3068af7cb8298391db4e1 Mon Sep 17 00:00:00 2001 From: Gabriel Pettier Date: Fri, 26 Oct 2018 16:34:20 +0200 Subject: [PATCH 2/7] add tests for aliasproperty cache fixes --- kivy/tests/test_properties.py | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/kivy/tests/test_properties.py b/kivy/tests/test_properties.py index 320422653d..e7bd5876f5 100644 --- a/kivy/tests/test_properties.py +++ b/kivy/tests/test_properties.py @@ -545,3 +545,96 @@ def test_color_property(self): self.assertEqual(color.get(wid), [1, 1, 0, 1]) color.set(wid, (1, 1, 0, 0)) self.assertEqual(color.get(wid), [1, 1, 0, 0]) + + def test_aliasproperty_cache_true_no_bind(self): + from kivy.properties import AliasProperty + + called = [0] + + def _get(wid): + called[0] += 1 + + def _set(wid, value): + return True + + alias = AliasProperty(_get, _set, cache=True) + alias.link(wid, 'alias') + called = [0] + assert called[0] == 0 + alias.set(wid, 1) + assert called[0] == 1 + alias.get(wid) + assert called[0] == 1 + + def test_aliasproperty_cache_true_bind(self): + from kivy.properties import NumericProperty, AliasProperty + from itertools import count + called = count() + + def _get(wid): + next(called) + return 1 + + def _set(wid, value): + return True + + wid.__class__.test_bind = test_bind = NumericProperty(100) + test_bind.link(wid, 'test_bind') + test_bind.link_deps(wid, 'test_bind') + + wid.__class__.alias = alias = AliasProperty(_get, _set, bind=['test_bind'], cache=True) + alias.link(wid, 'alias') + alias.link_deps(wid, 'alias') + + wid.test_bind = 0 + assert str(called) == 'count(1)' + alias.get(wid) + assert str(called) == 'count(1)' + wid.test_bind += 1 + assert str(called) == 'count(2)' + alias.get(wid) + assert str(called) == 'count(2)' + + def test_aliasproperty_cache_force_dispatch(self): + from kivy.properties import NumericProperty, AliasProperty + from itertools import count + called = count() + called_cb = count() + import q + + def _get(wid): + next(called) + return 1 + + def _set(wid, value): + return True + + def cb(wid, value): + q("called", value) + next(called_cb) + + wid.__class__.test_bind = test_bind = NumericProperty(100) + test_bind.link(wid, 'test_bind') + test_bind.link_deps(wid, 'test_bind') + + wid.__class__.alias = alias = AliasProperty( + _get, _set, bind=['test_bind'], + cache=True, + force_dispatch=True + ) + + alias.link(wid, 'alias') + alias.link_deps(wid, 'alias') + wid.bind(alias=cb) + + wid.test_bind = 0 + assert str(called) == 'count(1)' + assert str(called_cb) == 'count(1)' + alias.get(wid) + assert str(called) == 'count(1)' + wid.test_bind = 0 + assert str(called) == 'count(1)' + assert str(called_cb) == 'count(1)' + alias.get(wid) + assert str(called) == 'count(1)' + From 285d0c3b6815e6484c2da7495f4b755f994ff672 Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Wed, 31 Oct 2018 18:41:58 +0100 Subject: [PATCH 3/7] Added more tests for AliasProperty and group them together. --- kivy/tests/test_properties.py | 488 +++++++++++++++++++++------------- 1 file changed, 308 insertions(+), 180 deletions(-) diff --git a/kivy/tests/test_properties.py b/kivy/tests/test_properties.py index e7bd5876f5..db5a1a55b3 100644 --- a/kivy/tests/test_properties.py +++ b/kivy/tests/test_properties.py @@ -131,63 +131,6 @@ def test_propertynone(self): a.set(wid, 1) self.assertEqual(a.get(wid), 1) - def test_alias(self): - from kivy.properties import NumericProperty, AliasProperty - - wid.__class__.x = x = NumericProperty(0) - x.link(wid, 'x') - x.link_deps(wid, 'x') - wid.__class__.width = width = NumericProperty(100) - width.link(wid, 'width') - width.link_deps(wid, 'width') - - def get_right(self): - return x.get(self) + width.get(self) - - def set_right(self, value): - x.set(self, value - width.get(self)) - - right = AliasProperty(get_right, set_right, bind=('x', 'width')) - right.link(wid, 'right') - right.link_deps(wid, 'right') - - self.assertEqual(right.get(wid), 100) - x.set(wid, 500) - self.assertEqual(right.get(wid), 600) - width.set(wid, 50) - self.assertEqual(right.get(wid), 550) - - right.set(wid, 100) - self.assertEqual(width.get(wid), 50) - self.assertEqual(x.get(wid), 50) - - # test observer - global observe_called - observe_called = 0 - - def observe(obj, value): - global observe_called - observe_called = 1 - right.bind(wid, observe) - - x.set(wid, 100) - self.assertEqual(observe_called, 1) - observe_called = 0 - - x.set(wid, 100) - self.assertEqual(observe_called, 0) - - width.set(wid, 900) - self.assertEqual(observe_called, 1) - observe_called = 0 - - right.set(wid, 700) - self.assertEqual(observe_called, 1) - observe_called = 0 - - right.set(wid, 700) - self.assertEqual(observe_called, 0) - def test_reference(self): from kivy.properties import NumericProperty, ReferenceListProperty @@ -285,46 +228,6 @@ def observe(obj, value): x.get(wid).update({'bleh': 5}) self.assertEqual(observe_called, 1) - def test_aliasproperty_with_cache(self): - from kivy.properties import NumericProperty, AliasProperty - global observe_called - observe_called = 0 - - class CustomAlias(EventDispatcher): - basevalue = NumericProperty(1) - - def _get_prop(self): - global observe_called - observe_called += 1 - return self.basevalue * 2 - - def _set_prop(self, value): - self.basevalue = value / 2 - - prop = AliasProperty(_get_prop, _set_prop, - bind=('basevalue', ), cache=True) - - # initial checks - wid = CustomAlias() - self.assertEqual(observe_called, 0) - self.assertEqual(wid.basevalue, 1) - self.assertEqual(observe_called, 0) - - # change the base value, should trigger an update for the cache - wid.basevalue = 4 - self.assertEqual(observe_called, 1) - - # now read the value again, should use the cache - self.assertEqual(wid.prop, 8) - self.assertEqual(observe_called, 1) - - # change the prop itself, should trigger an update for the cache - wid.prop = 4 - self.assertEqual(observe_called, 2) - self.assertEqual(wid.basevalue, 2) - self.assertEqual(wid.prop, 4) - self.assertEqual(observe_called, 2) - def test_bounded_numeric_property(self): from kivy.properties import BoundedNumericProperty @@ -546,95 +449,320 @@ def test_color_property(self): color.set(wid, (1, 1, 0, 0)) self.assertEqual(color.get(wid), [1, 1, 0, 0]) - def test_aliasproperty_cache_true_no_bind(self): + def test_alias_property_without_setter(self): + from kivy.properties import AliasProperty + + expected_value = 5 + + class CustomAlias(EventDispatcher): + + def _get_prop(self): + self.getter_called += 1 + return expected_value + + prop = AliasProperty(_get_prop, None) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.getter_called = 0 + + # Initial checks + wid = CustomAlias() + self.assertEqual(wid.getter_called, 0) + + # Get value, should call getter once + value = wid.prop + self.assertEqual(value, expected_value) + self.assertEqual(wid.getter_called, 1) + + # Setter should raise an AttributeError + self.assertRaises(AttributeError, partial(setattr, wid, 'prop', 1)) + + def test_alias_property(self): + from kivy.properties import AliasProperty + + class CustomAlias(EventDispatcher): + + def _get_prop(self): + self.getter_called += 1 + + def _set_prop(self, value): + self.setter_called += 1 + + prop = AliasProperty(_get_prop, _set_prop) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.getter_called = 0 + self.setter_called = 0 + self.callback_called = 0 + + def callback(widget, value): + widget.callback_called += 1 + + # Initial checks + wid = CustomAlias() + wid.bind(prop=callback) + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 0) + self.assertEqual(wid.callback_called, 0) + + # Set property, should call setter to set the value + # Getter and callback should not be called because `_set_prop` doesn't + # returns True + wid.prop = 1 + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 1) + self.assertEqual(wid.callback_called, 0) + + # Set property to same value as before, should only call setter + wid.prop = 1 + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 2) + self.assertEqual(wid.callback_called, 0) + + # Get value of the property, should call getter once + self.assertEqual(wid.prop, None) + self.assertEqual(wid.getter_called, 1) + self.assertEqual(wid.setter_called, 2) + self.assertEqual(wid.callback_called, 0) + + def test_alias_property_cache_true(self): from kivy.properties import AliasProperty - called = [0] + expected_value = 5 - def _get(wid): - called[0] += 1 + class CustomAlias(EventDispatcher): - def _set(wid, value): - return True + def _get_prop(self): + self.getter_called += 1 + return expected_value - alias = AliasProperty(_get, _set, cache=True) - alias.link(wid, 'alias') - called = [0] - assert called[0] == 0 - alias.set(wid, 1) - assert called[0] == 1 - alias.get(wid) - assert called[0] == 1 + def _set_prop(self, value): + self.setter_called += 1 + return True - def test_aliasproperty_cache_true_bind(self): + prop = AliasProperty(_get_prop, _set_prop, cache=True) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.getter_called = 0 + self.setter_called = 0 + + # Initial checks + wid = CustomAlias() + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 0) + + # Get value of the property, should call getter once + value = wid.prop + self.assertEqual(value, expected_value) + self.assertEqual(wid.getter_called, 1) + self.assertEqual(wid.setter_called, 0) + + # Get value of the property, should return cached value + # Getter should not be called + value = wid.prop + self.assertEqual(value, expected_value) + self.assertEqual(wid.getter_called, 1) + self.assertEqual(wid.setter_called, 0) + + # Set value of property, should call getter and setter + wid.prop = 10 + value = wid.prop + self.assertEqual(value, expected_value) + self.assertEqual(wid.setter_called, 1) + self.assertEqual(wid.getter_called, 2) + + def test_alias_property_with_bind(self): from kivy.properties import NumericProperty, AliasProperty - from itertools import count - called = count() - - def _get(wid): - next(called) - return 1 - - def _set(wid, value): - return True - - wid.__class__.test_bind = test_bind = NumericProperty(100) - test_bind.link(wid, 'test_bind') - test_bind.link_deps(wid, 'test_bind') - - wid.__class__.alias = alias = AliasProperty(_get, _set, bind=['test_bind'], cache=True) - alias.link(wid, 'alias') - alias.link_deps(wid, 'alias') - - wid.test_bind = 0 - assert str(called) == 'count(1)' - alias.get(wid) - assert str(called) == 'count(1)' - wid.test_bind += 1 - assert str(called) == 'count(2)' - alias.get(wid) - assert str(called) == 'count(2)' - - def test_aliasproperty_cache_force_dispatch(self): + + class CustomAlias(EventDispatcher): + + x = NumericProperty(0) + width = NumericProperty(100) + + def get_right(self): + return self.x + self.width + + def set_right(self, value): + self.x = value - self.width + + right = AliasProperty(get_right, set_right, bind=('x', 'width')) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.callback_called = 0 + + # Assert values when setting x, width or right properties + wid = CustomAlias() + self.assertEqual(wid.right, 100) + wid.x = 500 + self.assertEqual(wid.right, 600) + wid.width = 50 + self.assertEqual(wid.right, 550) + wid.right = 100 + self.assertEqual(wid.width, 50) + self.assertEqual(wid.x, 50) + + def callback(widget, value): + widget.callback_called += 1 + + wid.bind(right=callback) + + # Callback should be called only when property changes + wid.x = 100 + self.assertEqual(wid.callback_called, 1) + wid.x = 100 + self.assertEqual(wid.callback_called, 1) + wid.width = 900 + self.assertEqual(wid.callback_called, 2) + wid.right = 700 + self.assertEqual(wid.callback_called, 3) + wid.right = 700 + self.assertEqual(wid.callback_called, 3) + + def test_alias_property_with_force_dispatch_true(self): + from kivy.properties import AliasProperty + + class CustomAlias(EventDispatcher): + + def _get_prop(self): + self.getter_called += 1 + + def _set_prop(self, value): + self.setter_called += 1 + + prop = AliasProperty(_get_prop, _set_prop, force_dispatch=True) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.getter_called = 0 + self.setter_called = 0 + self.callback_called = 0 + + def callback(widget, value): + widget.callback_called += 1 + + # Initial checks + wid = CustomAlias() + wid.bind(prop=callback) + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 0) + self.assertEqual(wid.callback_called, 0) + + # Set property, should call setter to set the value and getter to + # to get the value for dispatch call + wid.prop = 1 + self.assertEqual(wid.getter_called, 1) + self.assertEqual(wid.setter_called, 1) + self.assertEqual(wid.callback_called, 1) + + # Set property to same value as before, setter and getter and callback + # are called + wid.prop = 1 + self.assertEqual(wid.getter_called, 2) + self.assertEqual(wid.setter_called, 2) + self.assertEqual(wid.callback_called, 2) + + def test_alias_property_cache_true_with_bind(self): from kivy.properties import NumericProperty, AliasProperty - from itertools import count - called = count() - called_cb = count() - import q - - def _get(wid): - next(called) - return 1 - - def _set(wid, value): - return True - - def cb(wid, value): - q("called", value) - next(called_cb) - - wid.__class__.test_bind = test_bind = NumericProperty(100) - test_bind.link(wid, 'test_bind') - test_bind.link_deps(wid, 'test_bind') - - wid.__class__.alias = alias = AliasProperty( - _get, _set, bind=['test_bind'], - cache=True, - force_dispatch=True - ) - - alias.link(wid, 'alias') - alias.link_deps(wid, 'alias') - wid.bind(alias=cb) - - wid.test_bind = 0 - assert str(called) == 'count(1)' - assert str(called_cb) == 'count(1)' - alias.get(wid) - assert str(called) == 'count(1)' - wid.test_bind = 0 - assert str(called) == 'count(1)' - assert str(called_cb) == 'count(1)' - alias.get(wid) - assert str(called) == 'count(1)' + class CustomAlias(EventDispatcher): + + base_value = NumericProperty(1) + + def _get_prop(self): + self.getter_called += 1 + return self.base_value * 2 + + def _set_prop(self, value): + self.base_value = value / 2 + + prop = AliasProperty(_get_prop, _set_prop, + bind=('base_value',), + cache=True) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.getter_called = 0 + + # Initial checks + wid = CustomAlias() + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.base_value, 1) + self.assertEqual(wid.getter_called, 0) + + # Change the base value, should trigger an update for the cache + wid.base_value = 4 + self.assertEqual(wid.getter_called, 1) + + # Now read the value again, should use the cache + self.assertEqual(wid.prop, 8) + self.assertEqual(wid.getter_called, 1) + + # Change the prop itself, should trigger an update for the cache + wid.prop = 4 + self.assertEqual(wid.getter_called, 2) + self.assertEqual(wid.base_value, 2) + self.assertEqual(wid.prop, 4) + self.assertEqual(wid.getter_called, 2) + + def test_alias_property_cache_true_force_dispatch_true(self): + from kivy.properties import AliasProperty + + class CustomAlias(EventDispatcher): + + def _get_prop(self): + self.getter_called += 1 + return self.base_value * 2 + + def _set_prop(self, value): + self.setter_called += 1 + self.base_value = value / 2 + return True + + prop = AliasProperty(_get_prop, _set_prop, + cache=True, + force_dispatch=True) + + def __init__(self, **kwargs): + super(CustomAlias, self).__init__(**kwargs) + self.base_value = 1 + self.getter_called = 0 + self.setter_called = 0 + self.callback_called = 0 + + def callback(widget, value): + widget.callback_called += 1 + + wid = CustomAlias() + wid.bind(prop=callback) + + # Initial checks + self.assertEqual(wid.base_value, 1) + self.assertEqual(wid.getter_called, 0) + self.assertEqual(wid.setter_called, 0) + self.assertEqual(wid.callback_called, 0) + + # Set alias property some value, should call setter and then getter to + # pass the value to callback + wid.prop = 16 + self.assertEqual(wid.base_value, 8) + self.assertEqual(wid.getter_called, 1) + self.assertEqual(wid.setter_called, 1) + self.assertEqual(wid.callback_called, 1) + + # Same as the step above, should call setter, getter and callback + wid.prop = 16 + self.assertEqual(wid.base_value, 8) + self.assertEqual(wid.getter_called, 2) + self.assertEqual(wid.setter_called, 2) + self.assertEqual(wid.callback_called, 2) + + # Get the value of property, should use cached value + value = wid.prop + self.assertEqual(value, 16) + self.assertEqual(wid.getter_called, 2) + self.assertEqual(wid.setter_called, 2) + self.assertEqual(wid.callback_called, 2) From db82789ed62f591161f024d80443b0758ddf9fe0 Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Wed, 31 Oct 2018 19:54:54 +0100 Subject: [PATCH 4/7] Updated doc of AliasProperty. --- kivy/properties.pyx | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index 42687cefc5..d24cdd6c6a 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -1386,7 +1386,8 @@ cdef class AliasProperty(Property): If you don't find a Property class that fits to your needs, you can make your own by creating custom Python getter and setter methods. - Example from kivy/uix/widget.py:: + Example from kivy/uix/widget.py where x` and `width` are instances of + `NumericProperty`:: def get_right(self): return self.x + self.width @@ -1394,21 +1395,38 @@ cdef class AliasProperty(Property): self.x = value - self.width right = AliasProperty(get_right, set_right, bind=['x', 'width']) + If `x` were to be an instance level attribute and not Kivy property then + you have to return `True` from setter to dispatch value of `right`:: + + def set_right(self, value): + self.x = value - self.width + return True + + If your want to cache the value returned by getter then pass `cache=True`. + This way getter will only be called if new value is set or one of the + binded properties changes. In both cases new value of alias property will + be cached again. + + To make property readonly pass `None` as setter. This way `AttributeError` + will be raised on every set attempt:: + + right = AliasProperty(get_right, None, bind=['x', 'width'], cache=True) + :Parameters: `getter`: function - Function to use as a property getter + Function to use as a property getter. `setter`: function - Function to use as a property setter. Properties listening to the - alias property won't be updated when the property is set (e.g. - `right = 10`), unless the `setter` returns `True`. + Function to use as a property setter. Callbacks listening to the + alias property won't be called when the property is set (e.g. + `right = 10`), unless the setter returns `True`. `bind`: list/tuple - Properties to observe for changes, as property name strings + Properties to observe for changes, as property name strings. + Changing values of this properties will dispatch value of the + alias property. `cache`: boolean - If True, the value will be cached, until one of the binded - elements changes. If `bind` collection is empty and `cache` is - True, `setter` must return True in order to cache new value and - trigger its dispatch. - `rebind`: bool, defaults to False + If `True`, the value will be cached, until one of the binded + elements changes or if setter returns `True`. + `rebind`: bool, defaults to `False` See :class:`ObjectProperty` for details. .. versionchanged:: 1.9.0 From db6ae34cb46c911c1bc621e60e946cd6a194b71d Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Wed, 31 Oct 2018 20:16:04 +0100 Subject: [PATCH 5/7] Updated error message for readonly AliasProperty. --- kivy/properties.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index d24cdd6c6a..8157ef9908 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -1452,7 +1452,8 @@ cdef class AliasProperty(Property): self.use_cache = 1 def __read_only(self, _obj, _value): - raise AttributeError('property is read-only') + raise AttributeError('"{}.{}" property is readonly' + .format(type(_obj).__name__, self._name)) cdef init_storage(self, EventDispatcher obj, PropertyStorage storage): Property.init_storage(self, obj, storage) From 0c360d746e3369a11808a331cd47c2368ea5e3ed Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Mon, 5 Nov 2018 16:57:18 +0100 Subject: [PATCH 6/7] Wording change from "listening" to "bound" in setter description of AliasProperty. --- kivy/properties.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index 8157ef9908..4cb4b6b345 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -1416,7 +1416,7 @@ cdef class AliasProperty(Property): `getter`: function Function to use as a property getter. `setter`: function - Function to use as a property setter. Callbacks listening to the + Function to use as a property setter. Callbacks bound to the alias property won't be called when the property is set (e.g. `right = 10`), unless the setter returns `True`. `bind`: list/tuple From 5559faa2d70eccb7d285ca3c69ae39e2f81ed781 Mon Sep 17 00:00:00 2001 From: pythonic64 Date: Thu, 15 Nov 2018 15:28:47 +0100 Subject: [PATCH 7/7] Minor refactor and doc fix of AliasProperty. --- kivy/properties.pyx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kivy/properties.pyx b/kivy/properties.pyx index 4cb4b6b345..d47828ca56 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -1386,8 +1386,8 @@ cdef class AliasProperty(Property): If you don't find a Property class that fits to your needs, you can make your own by creating custom Python getter and setter methods. - Example from kivy/uix/widget.py where x` and `width` are instances of - `NumericProperty`:: + Example from kivy/uix/widget.py where `x` and `width` are instances of + :class:`NumericProperty`:: def get_right(self): return self.x + self.width @@ -1452,8 +1452,12 @@ cdef class AliasProperty(Property): self.use_cache = 1 def __read_only(self, _obj, _value): - raise AttributeError('"{}.{}" property is readonly' - .format(type(_obj).__name__, self._name)) + raise AttributeError( + '"{}.{}" property is readonly'.format( + type(_obj).__name__, + self._name + ) + ) cdef init_storage(self, EventDispatcher obj, PropertyStorage storage): Property.init_storage(self, obj, storage)