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 <matt@einhorn.dev>

* 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 <matt@einhorn.dev>
This commit is contained in:
Sander Land 2021-05-20 09:40:59 +02:00 committed by GitHub
parent c0800fa6f9
commit 28bfe768d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 1 deletions

View File

@ -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:

View File

@ -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")