I am messing with Cython, diving deeper into kivy and I have been trying my hand at making my own Kivy Property.
I have the following files difining a DocProperty: my.pyx:
from kivy.properties cimport Property, PropertyStorage
from kivy._event cimport EventDispatcher
cdef inline void observable_object_dispatch(object self, str name):
cdef Property prop = self.prop
prop.dispatch(self.obj, name)
class ObservableObject(object):
# Internal class to observe changes inside a native python object.
def __init__(self, *largs):
self.prop = largs[0]
self.obj = largs[1]
super(ObservableObject, self).__init__()
def __setattr__(self, name, value):
object.__setattr__(self, name, value)
observable_object_dispatch(self, name)
cdef class DocProperty(Property):
def __init__(self, defaultvalue=None, rebind=False, **kw):
self.baseclass = kw.get('baseclass', object)
super(DocProperty, self).__init__(defaultvalue, **kw)
self.rebind = rebind
cpdef link(self, EventDispatcher obj, str name):
Property.link(self, obj, name)
cdef PropertyStorage ps = obj.__storage[self._name]
ps.value = ObservableObject(self, obj, ps.value)
cdef check(self, EventDispatcher obj, value):
if Property.check(self, obj, value):
return True
if not isinstance(value, object):
raise ValueError('{}.{} accept only object based on {}'.format(
obj.__class__.__name__,
self.name,
self.baseclass.__name__))
cpdef dispatch(self, EventDispatcher obj, str name):
'''Dispatch the value change to all observers.
.. versionchanged:: 1.1.0
The method is now accessible from Python.
This can be used to force the dispatch of the property, even if the
value didn't change::
button = Button()
# get the Property class instance
prop = button.property('text')
# dispatch this property on the button instance
prop.dispatch(button)
'''
cdef PropertyStorage ps = obj.__storage[self._name]
ps.observers.dispatch(obj, ps.value, (name,), None, 0)
from kivy.properties cimport Property, PropertyStorage
from kivy._event cimport EventDispatcher
cdef class DocProperty(Property):
cdef object baseclass
cdef public int rebind
cpdef dispatch(self, EventDispatcher obj, str name)
And a quick sript to try it out: my.py
# -*- coding: utf-8 -*-
from kivy.event import EventDispatcher
import pyximport
pyximport.install()
from properties import DocProperty
if __name__ == '__main__':
class ED(EventDispatcher):
doc = DocProperty()
def on_doc(self, obj, value):
print 'printing doc', self.doc
class DumbObj(object):
def __init__(self, num):
self._num = num
@property
def num(self):
return 5
@num.setter
def num(self, value):
self._num = value
ed = ED()
ed.doc = DumbObj(3)
ed.doc.num = 4
When I run my.py, I get a 'Signature not compatible with previous declaration' on the dispatch method of DocProperty because I try override its declaration over at Property so it can accept one argument more than the original code declaration. Is it even possible to overload cpdef methods declared on pxd? If so, what am I doing wrong?
Edit:
Following @ead's suggestion, I tried replacing cpdef
statements with plain def
on the declarations of dispatch
, on both files and only one of them at a time. But that had no effect. I then tried to comment out the the call to dispatch to see what goes on if it didn't fail to compile. Turns out both attributes of DocProperty (baseclass and bind) raise AttributeError on assignment. Which is weird, because those were copy/pasted from Kivy source. This means my.pxd file is not having any effect on my Cython code? I tried from cimporting my.pxd into my.pyx, but that yielded not result also