From 28bfe768d8066b9b42d5e9081ba7318ae7ca5fbe Mon Sep 17 00:00:00 2001 From: Sander Land <48946947+sanderland@users.noreply.github.com> Date: Thu, 20 May 2021 09:40:59 +0200 Subject: [PATCH] EventDispatcher: Add nicer error message for non-existing properties (#7536) * nicer error message for unknown properties * edit kivy/tests/test_widget.py * edit kivy/_event.pyx * edit kivy/_event.pyx, edit kivy/tests/test_widget.py * edit kivy/_event.pyx * edit kivy/_event.pyx * edit kivy/tests/test_widget.py, edit kivy/_event.pyx * edit kivy/tests/test_widget.py * edit kivy/tests/test_widget.py * edit kivy/_event.pyx * more tests * test * test * edit kivy/_event.pyx * move tests * fix ALL the things * Update kivy/_event.pyx Co-authored-by: matham * edit kivy/_event.pyx * pytestify * remove kivy/graphics/cgl.h * edit kivy/tests/test_properties.py * edit kivy/tests/test_properties.py, edit kivy/_event.pyx * typeerror * fix Co-authored-by: matham --- kivy/_event.pyx | 14 +++++++- kivy/tests/test_properties.py | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/kivy/_event.pyx b/kivy/_event.pyx index 344f0c36c..ca6a073e8 100644 --- a/kivy/_event.pyx +++ b/kivy/_event.pyx @@ -228,7 +228,19 @@ cdef class EventDispatcher(ObjectWithUid): prop_args = { k: kwargs.pop(k) for k in list(kwargs.keys()) if k in properties} self._kwargs_applied_init = set(prop_args.keys()) if prop_args else set() - super(EventDispatcher, self).__init__(**kwargs) + + # at this point any kwargs passed may go to object + # which will raise a TypeError and cause confusion + try: + super(EventDispatcher, self).__init__(**kwargs) + except TypeError as e: + if kwargs and str(e).startswith("object.__init__() takes"): + raise TypeError( + 'Properties {} passed to __init__ may not be existing ' + 'property names. Valid properties are {}'.format( + sorted(kwargs.keys()), sorted(properties.keys()))) from e + else: + raise __cls__ = self.__class__ if __cls__ not in cache_events_handlers: diff --git a/kivy/tests/test_properties.py b/kivy/tests/test_properties.py index b444b4610..9e215d645 100644 --- a/kivy/tests/test_properties.py +++ b/kivy/tests/test_properties.py @@ -1266,3 +1266,67 @@ def test_inherit_property(): event.b = 'goodbye' assert event.b == 'goodbye' assert args == (event, 'goodbye') + + +def test_unknown_property(): + from kivy.properties import NumericProperty + + class MyWidget(EventDispatcher): + width = NumericProperty(0) + + with pytest.raises(TypeError) as cm: + MyWidget(width=12, unkn="abc") + assert "Properties ['unkn'] passed to __init__ may not be existing " \ + "property names. Valid properties are ['width']" \ + == str(cm.value) + + +def test_known_property_multiple_inheritance(): + + class Behavior: + def __init__(self, name): + print(f'Behavior: {self}, name={name}') + super().__init__() + + class Widget2(Behavior, EventDispatcher): + pass + + class Widget3(EventDispatcher, Behavior): + pass + + with pytest.raises(TypeError) as cm: + EventDispatcher(name='Pasta') + assert "Properties ['name'] passed to __init__ may not be existing" \ + in str(cm.value) + + Widget2(name='Pasta') # does not raise a ValueError + Widget3(name='Pasta') # does not raise a ValueError + + +def test_pass_other_typeerror(): + + class Behavior: + def __init__(self, name): + super().__init__() + raise TypeError("this is a typeerror unrelated to object") + + class Widget2(Behavior, EventDispatcher): + pass + + class Widget3(EventDispatcher, Behavior): + pass + + for cls in [Widget2, Widget3]: + with pytest.raises(TypeError) as cm: + cls(name='Pasta') + assert "this is a typeerror unrelated to object" == str(cm.value) + + +def test_object_init_error(): # the above 3 test rely on this + class TestCls(object): + def __init__(self, **kwargs): + super(TestCls, self).__init__(**kwargs) + + with pytest.raises(TypeError) as cm: + TestCls(name='foo') + assert str(cm.value).startswith("object.__init__() takes")