diff --git a/traits/has_traits.py b/traits/has_traits.py index 54420156ba..0453d0fc79 100644 --- a/traits/has_traits.py +++ b/traits/has_traits.py @@ -547,10 +547,10 @@ def __init__ ( self, cls, class_name, bases, class_dict, is_category ): prefix_list.append( name ) prefix_traits[ name ] = value - elif isinstance( value, FunctionType ) or is_cython_func_or_method(value): - pattern = getattr( value, 'on_trait_change', None ) - if pattern is not None: - listeners[ name ] = ( 'method', pattern ) + elif (( isinstance( value, FunctionType ) or + is_cython_func_or_method(value) ) and + hasattr( value, 'on_trait_change' )): + listeners[ name ] = ( 'method', value.on_trait_change ) elif isinstance( value, property ): class_traits[ name ] = generic_trait diff --git a/traits/tests/test_regression.py b/traits/tests/test_regression.py index b110fb0d36..cd03ca6930 100644 --- a/traits/tests/test_regression.py +++ b/traits/tests/test_regression.py @@ -11,7 +11,9 @@ from ..trait_numeric import Array from ..has_traits import HasTraits, Property, on_trait_change -from ..trait_types import Bool, DelegatesTo, Either, Instance, Int, List +from ..trait_types import ( + Bool, Callable, DelegatesTo, Either, Instance, Int, List +) from ..testing.unittest_tools import unittest @@ -232,5 +234,31 @@ def test_on_trait_change_with_list_of_extended_names(self): self.assertTrue(model.changed) +class TestDerivedClassDefaults(unittest.TestCase): + + def test_function_default(self): + # Regression test for enthought/traits#411. Providing a pure Python + # default in a child class for a Callable trait declared in the base + # class didn't work as expected -- the default value would be silently + # ignored. + + # Given + + def square(x): + return x ** 2 + + class A(HasTraits): + f = Callable() + + class B(A): + f = square + + # When + b = B() + + # Then + self.assertEquals(b.f(3), 9) + + if __name__ == '__main__': unittest.main()