Skip to content

Commit

Permalink
Merge pull request #14 from takluyver/delay-default-validation
Browse files Browse the repository at this point in the history
Ignore unspecified default value until first access
  • Loading branch information
minrk committed May 8, 2015
2 parents d93656b + af024cc commit df9c2f3
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 14 deletions.
17 changes: 17 additions & 0 deletions traitlets/tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,3 +1607,20 @@ class A(HasTraits):
a.x = 20
a.y = 20.0
self.assertEqual(len(self._notify1), 0)


def test_enum_no_default():
class C(HasTraits):
t = Enum(['a', 'b'])

c = C()
c.t = 'a'
assert c.t == 'a'

c = C()

with nt.assert_raises(TraitError):
t = c.t

c = C(t='b')
assert c.t == 'b'
42 changes: 28 additions & 14 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,20 @@ def init_default_value(self, obj):
obj._trait_values[self.name] = value
return value

def _setup_dynamic_initializer(self, obj):
# Check for a deferred initializer defined in the same class as the
# trait declaration or above.
mro = type(obj).mro()
meth_name = '_%s_default' % self.name
for cls in mro[:mro.index(self.this_class)+1]:
if meth_name in cls.__dict__:
break
else:
return False
# Complete the dynamic initialization.
obj._trait_dyn_inits[self.name] = meth_name
return True

def set_default_value(self, obj):
"""Set the default value on a per instance basis.
Expand All @@ -395,19 +409,17 @@ class has been instantiated.
The parent :class:`HasTraits` instance that has just been
created.
"""
# Check for a deferred initializer defined in the same class as the
# trait declaration or above.
mro = type(obj).mro()
meth_name = '_%s_default' % self.name
for cls in mro[:mro.index(self.this_class)+1]:
if meth_name in cls.__dict__:
break
else:
# We didn't find one. Do static initialization.
if not self._setup_dynamic_initializer(obj):
# We didn't find a dynamic initializer. Do static initialization.
self.init_default_value(obj)

def _set_default_value_at_instance_init(self, obj):
# As above, but if no default was specified, don't try to set it.
# If the trait is accessed before it is given a value, init_default_value
# will be called at that point.
if (not self._setup_dynamic_initializer(obj)) \
and (self.default_value is not Undefined):
self.init_default_value(obj)
return
# Complete the dynamic initialization.
obj._trait_dyn_inits[self.name] = meth_name

def __get__(self, obj, cls=None):
"""Get the value of the trait by self.name for the instance.
Expand Down Expand Up @@ -571,7 +583,7 @@ def __new__(cls, *args, **kw):
if isinstance(value, TraitType):
value.instance_init()
if key not in kw:
value.set_default_value(inst)
value._set_default_value_at_instance_init(inst)
inst._cross_validation_lock = False
return inst

Expand Down Expand Up @@ -1419,8 +1431,10 @@ def validate(self, obj, value):
class Enum(TraitType):
"""An enum that whose value must be in a given sequence."""

def __init__(self, values, default_value=None, **metadata):
def __init__(self, values, default_value=NoDefaultSpecified, **metadata):
self.values = values
if metadata.get('allow_none', False) and default_value is NoDefaultSpecified:
default_value = None
super(Enum, self).__init__(default_value, **metadata)

def validate(self, obj, value):
Expand Down

0 comments on commit df9c2f3

Please sign in to comment.