Source code for flexx.event._emitters
"""
Implementation of descriptors for generating events:
prop, readonly and emitter.
"""
import inspect
# Decorators to apply at a HasEvents class
def prop(func):
""" Decorator to define a settable propery. An event is emitted
when the property is set, which has values for "old_value" and
"new_value".
.. code-block:: python
class MyObject(event.HasEvents):
@prop
def foo(self, v=1):
''' docstring goes here. '''
return float(v)
m = MyObject(foo=2)
m.foo = 3
The method should have one argument, which can have a default
value to specify the initial value of the property. The body
of the method is used to do verification and normalization of the
value being set. The method's docstring is used as the property's
docstring.
"""
if not callable(func):
raise TypeError('prop decorator needs a callable')
return Property(func)
def readonly(func):
""" Decorator to define a readonly property. An event is emitted
when the property is set, which has values for "old_value" and
"new_value". To set a readonly property internally, use the
:func:`HasEvents._set_prop() <flexx.event.HasEvents._set_prop>` method.
.. code-block:: python
class MyObject(event.HasEvents):
@readonly
def bar(self, v=1):
return float(v)
m = MyObject()
m._set_prop('bar', 2) # only for internal use
"""
if not callable(func):
raise TypeError('readonly decorator needs a callable')
return Readonly(func)
def emitter(func):
""" Decorator to define an emitter. An emitter is an attribute that
makes it easy to emit specific events and functions as a placeholder
for documenting an event.
.. code-block:: python
class MyObject(event.HasEvents):
@emitter
def spam(self, v):
return dict(value=v)
m = MyObject()
m.spam(42)
The method can have any number of arguments, and should return a
dictionary that represents the event to generate. The method's
docstring is used as the emitter's docstring.
"""
if not callable(func):
raise TypeError('emitter decorator needs a callable')
return Emitter(func)
class BaseEmitter:
""" Base class for descriptors used for generating events.
"""
def __init__(self, func, name=None, doc=None):
assert callable(func)
self._func = func
self._name = name or func.__name__ # updated by HasEvents meta class
self.__doc__ = '*%s*: %s' % (self.__class__.__name__.lower(),
doc or func.__doc__ or self._name)
def __repr__(self):
cls_name = self.__class__.__name__
return '<%s for %s at 0x%x>' % (cls_name, self._name, id(self))
class Property(BaseEmitter):
""" A value that is gettable and settable.
"""
_SUFFIX = '_value'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._defaults = inspect.getargspec(self._func).defaults
def __set__(self, instance, value):
if isinstance is not None: # pragma: no cover
return instance._set_prop(self._name, value)
def __delete__(self, instance):
raise AttributeError('Cannot delete property %r.' % self._name)
def __get__(self, instance, owner):
if instance is None:
return self
private_name = '_' + self._name + self._SUFFIX
return getattr(instance, private_name)
class Readonly(Property):
""" A value that is gettable and only settable internally.
"""
def __set__(self, instance, value):
raise AttributeError("Can't set readonly property %r" % self._name)
class Emitter(BaseEmitter):
""" Placeholder for documentation and easy emitting of the event.
"""
def __set__(self, instance, value):
raise AttributeError("Can't set emitter attribute %r" % self._name)
def __delete__(self, instance):
raise AttributeError('Cannot delete emitter attribute %r.' % self._name)
def __get__(self, instance, owner):
if instance is None:
return self
func = instance._get_emitter(self._name)
func.__doc__ = self.__doc__
return func