enthought / traits

Observable typed attributes for Python classes
Other
431 stars 85 forks source link

Interfaces does not check traits types #384

Open aPere3 opened 6 years ago

aPere3 commented 6 years ago

I'm experimenting with Interfaces, and I noticed that @provides does not check for traits types:

from traits.api import Interface, Array, Float, HasTraits, provides
import traits

traits.has_traits.CHECK_INTERFACES = 2

class MyInterface(Interface):

    trait1 = Array(shape=[None,2])
    trait2 = Float()

@provides(MyInterface)
class TestClass1(HasTraits):
    trait1 = Array(shape=[10,2])
    trait2 = Float()

# This I expect to work, and it does.    

@provides(MyInterface)
class TestClass2(HasTraits):

    trait1 = Array(shape=[10,4])
    trait2 = Float()

# This I don't expect to fail, but it does not.

@provides(MyInterface)
class TestClass2(HasTraits):

    trait1 = Float()
    trait2 = Float()

# This I don't expect to fail either, but it does not.

Did I miss something ? What would it take to make traits type compatibility checking work ?

Thanks for this very nice library, by the way.

corranwebster commented 6 years ago

Thanks for the feedback. Checking that a class matches an interface isn't turned on by default because it can be expensive to do so (particularly at start up time). However it is available as an option. See http://docs.enthought.com/traits/traits_user_manual/advanced.html#implementing-an-interface

The basic idea is that you set the CHECK_INTERFACES flag in the traits.has_traits module to either 1 (which warns) or 2 (which raises an exception), eg.:

import traits.has_traits
traits.has_traits.CHECK_INTERFACES = 1
aPere3 commented 6 years ago

Thanks for the fast answer. I noticed this subtlety, and it was actually done in my example. Yet no Exception is raised when I run it. Is that normal ?

corranwebster commented 6 years ago

Apologies, I didn't notice that you had done that. Sorry for the noise.

So yes, checking the trait type may be reasonable, but may be hard to do in general (eg. if the interface specifies Float but an implementation specifies BaseFloat, for example). It is certainly something which might be worth considering (perhaps as a higher level check).

rkern commented 6 years ago

It's essentially impossible to do in general. Traits aren't a fully-fledged type system, so there's no general way to ask whether one type declaration is a subset of another. You can test whether the trait declarations are different, but most of the time, that's not an error but a feature (i.e. where the implementations choose a more restricted trait to provide its specific functionality).