Traits Rewritten

The company I work for, Enthought, has a nifty system they use in pretty much all their software called Traits ([url=https://svn.enthought.com/svn/enthought/Traits/tags/enthought.traits_2.0.1b1/docs/Traits2_UM.pdf]user manual — PDF!). It’s pretty cool, and it does some nifty stuff; they describe it as having five features:

[list=1][]Initialization
[
]Validation
[]Delegation
[
]Notification
[*]Visualization[/list]

Unfortunately, I don’t really like their implementation (it’s open-source, and there’s even a Trac installation for it), because it does a lot of what I consider magic. For example, check out Pg. 2 (the page numbered two, starting from the beginning of the first chapter) of the user manual linked above: [font=monospace]_age_changed[/font] is dynamically found and called, without the programmer ever specifying that it should be used. That doesn’t really seem Pythonic.

So, I wrote it from scratch. They won’t use my system because it doesn’t have seven years of bug-fixes and tests (although many of their actual unittests fail), but I did it more to see if I could, anyways. :stuck_out_tongue:

I didn’t rewrite the visualization part, though, because I couldn’t care less about that, and I wasn’t really interested in trying to replicate that beautifully.

I’ve posted my version at my site: http://code.nokrev.com/?p=traits-rewritten.git, but I’ve reproduced some examples (based of the tests) below, so as to provide a basic feel for what it does.

from traits.base import Trait, String

class Traited(object):
    x = Trait() # Value defaults to None
    s = String() # Defaults to ''

if __name__ == '__main__':
    t = Traited()
    print t.x, t.s # => 'None '

    t.x = 'Some value'
    t.s = 'A string'
    print t.x, t.s # => 'Some value (here, a str) A string'

Here’s an example of delegation (the child’s last name is the father’s last name). Unfortunately, I have not found a way to implement this without resorting to magic, so I still am unhappy with how this actually works (but I am open to suggestions):

from traits.base import Trait, String
from traits.delegation import Delegate

class Parent(object):
    first_name = String()
    last_name = String()

class Child(object):
    father = Delegate(Parent)

    first_name = String()
    last_name = father.last_name

if __name__ == '__main__':
    dad = Parent()
    dad.first_name = 'Daddy'
    dad.last_name = 'Wheeler'

    son = Child()
    son.father = dad
    son.first_name = 'Jeff'

    print son.last_name # => 'Wheeler'

Some 4th of July validation:

from traits.base import Trait, String, validate

class Flag(object):
    color = String(default='white')

    @validate(color)
    def name_validator(self, trait, color):
        if color.lower() not in ('red', 'white', 'blue'):
            raise ValueError('Color must be "red", "white", or "blue"')

if __name__ == '__main__':
    c = Flag()

    try:
        c.color = 'Green'
    except ValueError:
        print 'Could not set color to "Green"'

    c.color = 'Red'
    c.color = 'white'

I think you get the idea … it does some other cool stuff too, and even supports multiple inheritance. :smiley:

I came into this thread expecting some implementation of traits.

_age_changed is dynamically found and called, without the programmer ever specifying that it should be used.

That does seem fairly suspicious.

Your rewritten source seems fairly short and clean… especially compared to the pieces of their source that I looked through. However, I very rarely use Python, so I’m probably not the best judge of differences. :stuck_out_tongue:

I think the [u]Zen of Python[/u] explains why it’s awkward pretty well:

:stuck_out_tongue:

^ I feel smart since I already knew the command to show the Zen.

[QUOTE=Jeff Wheeler;2353843]I think you get the idea … [/QUOTE]
No, no I don’t :stuck_out_tongue:

You know too much about stuff like this.

Basically, it lets you define classes with “smart” properties. They can have default values, and whenever they’re being edited, they can be validated. Listener classes can subscribe to events, too, so you can have a method that gets called whenever a property is changed or set (set is called when it’s set to the value it already was, while changed is not).

Also, delegation lets you refer to other objects and do cool stuff. :smiley:

So, they let you do fancy properties that can be hooked into.