Picon
Gravatar

Re: [Traits] dirty trait metadata

Robert Kern wrote:

>> Is there a simple way of doing this?  I know that I could achieve this
>> if I implemented x as a Property(Array) but is there an easier way?
> 
> I don't think so. The comparison logic is in C
> (ctraits.c:setattr_trait), and there are no hooks for this.
> 
> If you only need coarse-grained notifications, you might consider
> using an updated Event. This:

OK, so I came up with a new trait type that does this nicely with 
minimal fuss and also does the validation right.  Attached is the code 
along with a complete test case.  A quick example:

ArrayOrNone = Either(None, Array)
class Data(HasTraits):
     x = ShadowProperty(ArrayOrNone)
     # Test attribute.
     _test = Any
     def _x_changed(self, value):
         self._test = value.copy()

This automatically builds the shadow _x trait and always calls back 
_x_changed.  The ShadowProperty can be made smarter so it optionally 
checks to see if the value changed and only then fire the handler but 
thats easy to do.  I'll likely do it.  I think this is a common enough 
use case this solves (the shadow getter/setter stuff) that this may be a 
worthy addition to traits.  Maybe it is already there somewhere?

cheers,
prabhu

from enthought.traits.api import (TraitType, Instance, CTrait)
from enthought.traits.traits import trait_cast
import numpy

class ShadowProperty(TraitType):

    trait_type = Instance(CTrait)

    def __init__(self, trait_type, **metadata):
        self.trait_type = trait_cast(trait_type)
        super(ShadowProperty, self).__init__(**metadata)

    def validate(self, object, name, value):
        value = self.trait_type.validate(object, name, value)
        return value

    def get(self, object, name):
        shadow = self._get_shadow(name)
        d = object.__dict__
        if shadow in d:
            return d[shadow]
        else:
            return None

    def set(self, object, name, value):
        old = self.get(object, name)
        shadow = self._get_shadow(name)
        object.__dict__[shadow] = value
        # Fire a trait property changed.
        object.trait_property_changed(name, old, value)

    def _get_shadow(self, name):
        return '_' + name

# -------------- test ---
from enthought.traits.api import HasTraits, Either, Array, Any, TraitError
import unittest

ArrayOrNone = Either(None, Array)
class Data(HasTraits):
    x = ShadowProperty(ArrayOrNone)
    # Test attribute.
    _test = Any
    def _x_changed(self, value):
        self._test = value.copy()

class TestShadowProperty(unittest.TestCase):
    def test_shadow_property(self):
        "Test if the shadow property trait type works correctly."
        x = numpy.linspace(0, 1)
        d = Data(x=x)
        self.assertEqual(numpy.all(d.x == x), True)
        self.assertEqual(numpy.all(d._x == x), True)
        self.assertEqual(numpy.all(d._test == x), True)
        x *= 2
        d.x = x
        self.assertEqual(numpy.all(d.x == x), True)
        self.assertEqual(numpy.all(d._x == x), True)
        self.assertEqual(numpy.all(d._test == x), True)

    def test_type_checking(self):
        "Test if the validation works correctly."
        x = numpy.linspace(0, 1)
        d = Data(x=x)
        self.assertRaises(TraitError, d.__setattr__, 'x', 'hey')

if __name__ == '__main__':
    unittest.main()

_______________________________________________
Enthought-dev mailing list
Enthought-dev@...
https://mail.enthought.com/mailman/listinfo/enthought-dev

Gmane