classification
Title: Deleting attribute of Enum gives misleading error message
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: ethan.furman Nosy List: barry, eli.bendersky, ethan.furman, python-dev, r.david.murray, vajrasky
Priority: normal Keywords: patch

Created on 2013-09-15 15:15 by vajrasky, last changed 2013-09-22 23:18 by python-dev. This issue is now closed.

Files
File name Uploaded Description Edit
handling_attribute_deletion_correctly_in_enum.patch vajrasky, 2013-09-15 15:15 review
unit_test_attribute_deletion_enum.patch vajrasky, 2013-09-16 04:40 review
Messages (7)
msg197775 - (view) Author: Vajrasky Kok (vajrasky) * Date: 2013-09-15 15:15
>>> from enum import Enum
>>> class MyPet(Enum):
...   CUTE_CAT = 1
...   VIGOROUS_DOG = 2
...   UGLY_BLOBFISH = 3
...   PROMISCUOUS_BONOBO = 4
...   def spam(cls): pass
... 
>>> del MyPet.CUTE_CAT
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: CUTE_CAT

This is misleading. I have CUTE_CAT attribute in Enum. It should throw TypeError exception.

>>> del MyPet.LONELY_WOLF
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: LONELY_WOLF

This is correct.

>>> del MyPet['CUTE_CAT']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'EnumMeta' object does not support item deletion

I think this behaviour is correct.

>>> del MyPet['LONELY_WOLF']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'EnumMeta' object does not support item deletion

This behaviour is somewhat misleading. I don't have LONELY_WOLF item.

>>> del MyPet.spam
>>> hasattr(MyPet, 'spam')
False

This is correct. We should be able to delete method from Enum class, or shouldn't we?

>>> cute_cat = MyPet.CUTE_CAT
>>> del cute_cat.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/sky/Code/python/programming_language/cpython/Lib/enum.py", line 29, in __delete__
    raise AttributeError("can't delete attribute")
AttributeError: can't delete attribute

This is *maybe" correct. But shouldn't it be TypeError exception?

All the exception messages related with deleting attribute in Python codebase are using TypeError exception (with one exception in pyexpat on which it uses RuntimeError exception).

Attached the patch to handle the attribute deletion correctly.

For now, I don't change the exception for the last case (del MyPet.CUTE_CAT.name) because maybe there is an exception case for enum.
msg197780 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2013-09-15 16:04
Doesn't this confusion (here and in 19011) arise from the fact that the enum class does *not* have CUTE_CAT attribute?  That is, the error message is correct, but surprising.  Because, frankly, Enums are surprising in many ways ;)
msg197801 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-09-15 17:50
Perhaps a section in the docs about the differences from typical Python classes is warranted:

  - Enum members are virtual
  - Enum members are singletons
  - new Enum members (aka instances of an Enum class) cannot be created
  - during class creation Enum members cannot be overwritten, nor overwrite
    other class attributes
  - Enum classes with members cannot be subclassed
  - Enum classes support iteration
  - Enum classes support containment
msg197802 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-09-15 18:01
As for the error messages (going in reverse order):

==========================================================================
--> del cute_cat.name
Traceback (most recent call last):
...
AttributeError: can't delete attribute
==========================================================================

This is the same error given by @property.


==========================================================================
--> del MyPet.spam
--> hasattr(MyPet, 'spam')
False
==========================================================================

This is correct.  Only Enum members get special treatment.


==========================================================================
--> del MyPet['LONELY_WOLF']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'EnumMeta' object does not support item deletion
==========================================================================

There are two errors here: LONELY_WOLF does not exist, and an operation is being attempted that is not supported.  Of those two, attempting the unsupported operation is the more serious, and should be the reported error.  (Yes, dealing with multiple errors at once can often be confusing.)


==========================================================================
--> del MyPet.CUTE_CAT
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: CUTE_CAT
==========================================================================

This one I am not sure about (much as I am not sure about __getattr__ in #19011).  Enum members are virtual, and don't actually live in the class namespace.  I'm inclined to leave the existing behavior as is, but if we choose to change one, we should change both.
msg197868 - (view) Author: Vajrasky Kok (vajrasky) * Date: 2013-09-16 04:40
Here is the patch containing unit test only to confirm existing behaviour. So people can learn what to expect when they delete Enum attributes.

Even if we *decide* to change the behaviour of "del MyPet.CUTE_CAT" (assuming CUTE_CAT is an Enum member), I sense that we would only change the exception message not the exception type. That is, it still throws AttributeError but with better error message, such as "Cannot delete attribute."
msg198307 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2013-09-22 23:15
Okay, I changed my mind about __delattr__.  Having it say "AttributeError: cannot delete Enum member" is certainly nicer than "AttributeError: MemberName"
msg198308 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2013-09-22 23:18
New changeset ed011b0d7daf by Ethan Furman in branch 'default':
Close #19025: Better error message when trying to delete an Enum member.
http://hg.python.org/cpython/rev/ed011b0d7daf
History
Date User Action Args
2013-09-22 23:18:38python-devsetstatus: open -> closed

nosy: + python-dev
messages: + msg198308

resolution: fixed
stage: resolved
2013-09-22 23:15:07ethan.furmansetmessages: + msg198307
2013-09-16 04:40:08vajraskysetfiles: + unit_test_attribute_deletion_enum.patch

messages: + msg197868
2013-09-15 18:01:03ethan.furmansetmessages: + msg197802
2013-09-15 17:50:38ethan.furmansetnosy: + eli.bendersky
messages: + msg197801
2013-09-15 16:34:11ethan.furmansetassignee: ethan.furman
2013-09-15 16:28:00barrysetnosy: + barry
2013-09-15 16:04:30r.david.murraysetnosy: + r.david.murray
messages: + msg197780
2013-09-15 15:15:10vajraskycreate