Skip to content


Subversion checkout URL

You can clone with
Download ZIP


errorvalue and errohandler attributes added to the Property base class #637

merged 3 commits into from

7 participants


I incorporated the proposed changes in properties.pyx and properties.pxd.

I did not modify the documentation, but if necessary I can propose new docs.

This is my first pull request so apologies in advance if I've screwed up.


Yep, documentation along your new changes are necessary :)
Don't forget to add the "versionadded" directive! (grep it)


technically its possible to set both errorvalue and errorhandler the way init is set up. in that case, does it make sense, to change this to:

    if errorvalue_set == 1:
        value = errorvalue
        self.check(obj, value)
        raise e
except ValueError as error:
     if errorhandler is not None:
        value = errorhandler(value)
        raise error

of course this would only happen if the errorvalue specified in init also is invalid, and an errorhandler is set. maybe it makes more sense to either not alloow setting both, or only have one "errorvalue" property and allow passing a function (although that may be problematic for ObjectProperty, since user may intend to pass a function as the itself, in which case that could only be done by passing a errorvalue function that returns the iintended function/callable)


Ah yes, it's true, they should be exclusive.

Using a single parameter is clever, but as you point out, if the user intends to provide a function from an ObjectProperty, there's no way to tell (something I would have overlooked until you noted it).

Another option is to simply do away with errorvalue, but it seems very convenient, common, and readable.

Otherwise I don't see an alternative to two parameters. In light of this, I could throw an error in init if:

  1. both errorvalue and errorhandler are set

... which is fine, but it makes me wonder if I shouldn't also test ...

  1. if errorvalue is valid
  2. if errorhandler is a function that takes one argument

But one small concern on 1. If errorvalue just overrides (or errorhandler, don't care), it could make runtime swapping safer. In other words, if the errorvalue or errorhandler can both be on at the same time, it's easier to toggle.

Properties are so primitive to Kivy/Kv that I'm happy to spend the time getting the API right.


@willheger: See my comments in the dev mailing list. Probably largely irrelevant considering the amount of work that has been done now, but may be intereseting none-the-less.


errorvalue and errorhandler now have documentation and a little constructor validation. Ideally an init validator would check:

errorhandler - this is a single argument callable (function or lambda). This sounds easy, but it's actually quite difficult to verify the number arguments in Python. The inspect module will tell you how many arguments a function will take, but it will not tell you what if any default arguments, such as 'self' are provided. The second option is to just test the function with a dummy value hoping to catch a "TypeError: x() takes exactly 1 argument" exception. This is also challenging to determine if the TypeError was actually thrown by the callable or something internal. It would be nice, but I gave up and left it as simply testing for 'callable.'

errorvalue - obviously the upside of using errorvalue should be testing at creation time, but I don't see a way to run the subclass's check() method within the init of the Property base class. I'm just not clear on how you pass the Descriptor 'object' argument from within init and I found the cython documentation lacking.

Finally, me and git.


Finally, this is a quick test app.

import kivy

from import App
from kivy.uix.floatlayout import FloatLayout
from import BoundedNumericProperty

class Base(FloatLayout):
    bnp = BoundedNumericProperty(0, min=-500, max=500, errorvalue=10)
    bnp = BoundedNumericProperty(0, min=-500, max=500, 
        errorhandler=lambda x: 500 if x > 500 else -500)

class TestApp(App):

    def build(self):
        b = Base()
        return b

#:kivy 1.0.4
    bnp: control.value

        id: control
        min: -1000
        max: 1000
        value: 0

        y: 20
        text: str(root.bnp)

If you want a quicky and good understanding of how git works, i find this visual guide to be pretty helpful :)


Also this 10min tuto is very helpful :


I believe this pull accomplished the goal, I just don't know what other steps I must fulfill.

1. I provided inline docs, but the versions are two back since I committed, so I guess that would need to be updated.
2. There's a little test app in the thread, but I did not submit any unit tests, should I?
3. Is there anything else I can do?

@tshirtman tshirtman merged commit 4a357f3 into kivy:master

i added two unittests based on the example, updated the versionchanged, there was a conflict that seems harmless, all seems good, thanks, and sorry for the delay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 10, 2012
Commits on Aug 25, 2012
  1. Merge remote branch 'upstream/master'

    will authored
This page is out of date. Refresh to see the latest.
Showing with 66 additions and 1 deletion.
  1. +3 −0  kivy/properties.pxd
  2. +63 −1 kivy/properties.pyx
3  kivy/properties.pxd
@@ -2,6 +2,9 @@ cdef class Property:
cdef str _name
cdef int allownone
cdef object defaultvalue
+ cdef object errorvalue
+ cdef object errorhandler
+ cdef int errorvalue_set
cdef init_storage(self, dict storage)
cpdef link(self, object obj, str name)
cpdef link_deps(self, object obj, str name)
64 kivy/properties.pyx
@@ -83,6 +83,27 @@ With Kivy, you can simplify like this::
That's all!
+Error Handling
+If setting a value would otherwise raise a ValueError, you have two options to
+handle the error gracefully within the property. An errorvalue is a substitute
+for the invalid value. An errorhandler is a callable (single argument function
+or lambda) which can return a valid substitute::
+errorhandler parameter::
+ # simply returns 0 if the value exceeds the bounds
+ bnp = BoundedNumericProperty(0, min=-500, max=500, errorvalue=0)
+errorvalue parameter::
+ # returns a the boundary value when exceeded
+ bnp = BoundedNumericProperty(0, min=-500, max=500,
+ errorhandler=lambda x: 500 if x > 500 else -500)
@@ -182,16 +203,38 @@ cdef class Property:
a = MyObject()
a.hello = 'bleh' # working
a.hello = None # working too, because allownone is True.
+ :Parameters:
+ `errorhandler`: callable
+ If set, must take a single argument and return a valid substitute value
+ `errorvalue`: object
+ If set, will replace an invalid property value (overrides errorhandler)
+ .. versionchanged:: 1.4.0
+ Parameters errorhandler and errorvalue added
def __cinit__(self):
self._name = ''
self.allownone = 0
self.defaultvalue = None
+ self.errorvalue = None
+ self.errorhandler = None
+ self.errorvalue_set = 0
def __init__(self, defaultvalue, **kw):
self.defaultvalue = defaultvalue
self.allownone = <int>kw.get('allownone', 0)
+ self.errorvalue = kw.get('errorvalue', None)
+ self.errorhandler = kw.get('errorhandler', None)
+ if 'errorvalue' in kw:
+ self.errorvalue_set = 1
+ if 'errorhandler' in kw and not callable(self.errorhandler):
+ raise ValueError('errorhandler %s not callable' % self.errorhandler)
property name:
def __get__(self):
@@ -200,8 +243,12 @@ cdef class Property:
cdef init_storage(self, dict storage):
storage['value'] = self.defaultvalue
storage['allownone'] = self.allownone
+ storage['errorvalue'] = self.errorvalue
+ storage['errorhandler'] = self.errorhandler
+ storage['errorvalue_set'] = self.errorvalue_set
storage['observers'] = []
cpdef link(self, object obj, str name):
'''Link the instance with its real name.
@@ -263,7 +310,22 @@ cdef class Property:
realvalue = d['value']
if self.compare_value(realvalue, value):
return False
- self.check(obj, value)
+ try:
+ self.check(obj, value)
+ except ValueError as e:
+ errorvalue = obj.__storage[self._name]['errorvalue']
+ errorhandler = obj.__storage[self._name]['errorhandler']
+ errorvalue_set = obj.__storage[self._name]['errorvalue_set']
+ if errorvalue_set == 1:
+ value = errorvalue
+ self.check(obj, value)
+ elif errorhandler is not None:
+ value = errorhandler(value)
+ self.check(obj, value)
+ else:
+ raise e
d['value'] = value
return True
Something went wrong with that request. Please try again.