traits icon indicating copy to clipboard operation
traits copied to clipboard

class_traits uses __base_traits__ instead of __class_traits__

Open kitchoi opened this issue 4 years ago • 5 comments

I came across the following base class and subclass:

class Base(HasTraits):
    a = Str("a")

class Child(Base):
    a = "overridden"

If you try to inspect the default value for Child.a:

> Child.class_traits()["a"].default
"a"

But I'd expect "overridden" to be returned instead.

If I access __class_traits__ instead, I get the value I expected:

> Child.__class_traits__["a"].default
"overridden"

(It was new to me that redefining the traits in a subclass is considered as redefining the traits with a new default value. See this branch.)

Turns out HasTraits.class_traits is using __base_traits__, not __class_traits__: https://github.com/enthought/traits/blob/ba7c10fa28666219e276c7eeeac7e3e7168a45c2/traits/has_traits.py#L3085-L3094

Is this intended? Or is this a bug?

kitchoi avatar Dec 18 '19 18:12 kitchoi

My suspicion is that maybe https://github.com/enthought/traits/blob/ba7c10fa28666219e276c7eeeac7e3e7168a45c2/traits/has_traits.py#L633 should be setting into the base trait dict as well as the class trait dict? Either that or class_traits should be using __class_traits__ as you suggest. But I'm not yet sure enough which of them is "right".

corranwebster avatar Dec 19 '19 08:12 corranwebster

One thing I'd like to do is expose the actual class_traits and instance_traits dictionaries (the ctrait_dict and itrait_dict fields on a C has_traits_object) that CHasTraits is using, if only for debugging purposes. I don't think there's currently any clean way to get those dictionaries. More generally, as much as possible of cTrait and CHasTraits should be introspectable at some level.

mdickinson avatar Dec 19 '19 10:12 mdickinson

And as far as I can tell, __base_traits__ and __class_traits__ are intentionally different in that __base_traits__ are the ones used when subclassing. I'm not convinced that the line @corranwebster identifies should be changed, but I do think we should probably link the class_traits method to the __class_traits__ dictionary rather than the __base_traits__ dictionary. It would be helpful to go through ETS and identify uses of the class_traits method to see if any of them would be adversely affected by such a change.

mdickinson avatar Dec 19 '19 10:12 mdickinson

Yes I was under the same impression that __base_traits__ refers to the traits defined in the base classes, whereas __class_traits__ refers to the traits on the current(?) class.

I am wondering (going down the rabbit hole)... is HasTraits.traits affected too? This line: https://github.com/enthought/traits/blob/ba7c10fa28666219e276c7eeeac7e3e7168a45c2/traits/has_traits.py#L3018

kitchoi avatar Dec 19 '19 10:12 kitchoi

is HasTraits.traits affected too?

Likely, yes. The traits that a HasTraits instance is actually using on attribute lookup are in the ctrait_dict and itrait_dict fields; I'd expect those dictionaries to be the ones being used when reporting either class_traits or traits.

mdickinson avatar Dec 19 '19 10:12 mdickinson