Skip to content

Take default value for Enum from allowed values#13

Closed
takluyver wants to merge 1 commit intoipython:masterfrom
takluyver:enum-default-value
Closed

Take default value for Enum from allowed values#13
takluyver wants to merge 1 commit intoipython:masterfrom
takluyver:enum-default-value

Conversation

@takluyver
Copy link
Member

If no default is explicitly specified and allow_none is False (the default), Enum currently causes an error if you instantiate the object owning it without supplying a value for it. E.g.

class ExecuteReply(Reference):
    execution_count = Integer()
    status = Enum((u'ok', u'error'))
    ...

ExecuteReply()  # TraitError

This changes makes it take the first of the possible values as the default instead. If you still want to force the code instantiating the class to supply a value, you can create the Enum with default_value=None.

If no default is explicitly specified and allow_none is False (the
default), Enum currently causes an error if you instantiate the object
owning it without supplying a value for it.

This changes makes it take the first of the possible values as the
default instead. If you want to force the code instantiating the class
to supply a value, you can create the Enum with `default_value=None`.
@ssanderson
Copy link
Member

-1 on this from me. As a user of traitlets for various random side projects, I'd always rather have to explicitly specify a default value than have the traitlet system try to guess what it thinks I might have meant. The failure mode when you forget to specify a default is easy to reproduce and straightforward to diagnose from the resulting stack trace. The failure mode when a traitlet unexpectedly has a default is that a value somewhere in your program has a value you didn't mean to supply, which is much more likely to cause unexpected behavior that's not easily traceable back to the root cause.

@SylvainCorlay
Copy link
Member

I get Scott's argument on defensive programming. Although I would be +1 on this. I wanted to open the same PR also because HasTraits declarations with decorations gets longer and longer. Removing unnecessary arguments is beneficiary in that regard. Besides it is consistent with the documentation format for tuples where the first value is the default.

@takluyver
Copy link
Member Author

The reason I ran into this is for https://github.com/jupyter/kerneltest, where traitlets are used to validate messages. All of the instances are created without specifying any values, and then the values from the messages are assigned to instance attributes to validate them. It seems wrong to specify a default value just to shut traitlets up, and it's definitely wrong to set allow_none=True, because it shouldn't accept None later on.

This is admittedly something of an odd case, but I think the change is in keeping with the way traitlets generally work - if you specify List() without a default value, you get a default empty list. Int() without a specified default has a default value of 0. TCPAddress() has a default value of ('127.0.0.1', 0). I can't immediately think of another trait type where there's an obvious way to specify it so that you have to give a value for it at object instantiation. And I think this was historically true of Enum as well, until the default for allow_none was switched to False.

However, I understand the defensive programming argument as well. Maybe it would be a useful compromise if it threw an error when you access the traitlet without having given it a value, rather than when you instantiate the object? I think that would work for my use case.

@SylvainCorlay
Copy link
Member

@minrk proposed to postpone the instantiation of default values (and therefore their validation) to the first time it is accessed. It would probably make sense for your use case.

@minrk
Copy link
Member

minrk commented Apr 28, 2015

In the simple case of a list or tuple, I think picking the first element by default is clear and correct behavior. But when it's a set, the initial value would be randomized, which isn't good.

The fact that the initial value is different whether allow_none is True or not suggests that this is hazardous, too. If you can delay the raise until first access prior to assignment, that would solve the problem without adding any inappropriate guessing.

@SylvainCorlay
Copy link
Member

Hum, I don't understand how the default value should depend on whether allow_none is True or False?

@minrk
Copy link
Member

minrk commented Apr 28, 2015

@SylvainCorlay in this implementation as it is, when allow_none is True, the default value is None. Otherwise, the default value is the first element. At the very least, I don't think the default value should depend on whether None is allowed.

@SylvainCorlay
Copy link
Member

Ok, then I guess it would make sense that the default value is the first one of the options in both cases.
(I don't think there are many cases where allow_none would True for Enums.)

takluyver added a commit to takluyver/traitlets that referenced this pull request Apr 29, 2015
Alternative to ipythongh-13

Previously, if:

- The traitlet class does not specify a default value,
- The use of the traitlet does not provide a default value,
- The class using the traitlet does not have a _foo_default method, and
- The instantiation of that class does not provide a value for the
traitlet

then the traitlet would try to set its value to the undefined default,
usually causing an error. With this change, it will skip that, so the
traitlet will have no value. If it is accessed before a value has been
set, the error will occur then instead.

This allows Enums with no default value, which I want for kernelspec.

`Instance` traitlets are not affected - they will still create an
instance of the specified class when the class using them is
instantiated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do

if default_value is NoDefaultSpecified:
    if self.values:
        default_value = next(iter(self.values))
    else:
        default_value  = Undefined

and have it all not depend and allow_none.

@takluyver
Copy link
Member Author

Superseded by #14.

@takluyver takluyver closed this May 8, 2015
@takluyver takluyver deleted the enum-default-value branch May 8, 2015 21:45
@minrk minrk modified the milestone: no action Nov 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants