This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Enum._missing_ not called for __getitem__ failures
Type: behavior Stage: resolved
Components: Versions: Python 3.7
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: ethan.furman Nosy List: barry, eli.bendersky, ethan.furman, josh.r
Priority: normal Keywords:

Created on 2017-03-07 22:29 by ethan.furman, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (6)
msg289191 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2017-03-07 22:29
class Label(Enum):

    RedApple = 1
    GreenApple = 2

    @classmethod
    def _missing_(cls, name):
        for member in cls:
            if member.name.lower() == name.lower():
                return member

Currently, _missing_ is only called when using the functional API. In words:

Label('redapple')  # works
Label.redapple     # does not
msg289241 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2017-03-08 15:31
Could that perhaps be intentional? Attribute access seems like something where the developer would be explicitly naming a single, hard coded, canonical name for the type, while string construction seems like something where you're getting a string from "somewhere" (user input, which is always terrible) and you'd want to have a way to handle invalid input.

The documentation is so sparse as to be useless for determining intent:

_missing_ – a lookup function used when a value is not found; may be overridden

I wouldn't view Label.redapple as an attempt to "find" anything, it's just simple attribute access.
msg289306 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2017-03-09 16:47
Thank you, Josh, that's a very good point.

One can be expected to have the correct spelling when using attribute access.

So the two accesses that make sense for a _missing_ call would then be:

- by-value lookup (e.g. Label(1))

- by-name lookup (e.g. Label['redapple'])

Sound reasonable?
msg289331 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2017-03-10 01:26
Yeah, that sounds fine.
msg310253 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-01-18 19:28
To move this forward:

The proposal is to add support for a new method, _missing_name_, which is called by __getitem__.

If such a method does not exist, the normal AttributeError exception is raised;

otherwise, the _missing_name_ method is called with the invalid name and should return a matching member or None;
- if None, the normal AttributeError exception is raised
- if a member, it is returned
- otherwise, a Type(?)Error is raised
msg310401 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2018-01-22 07:47
I'm not convinced this piece needs to be in the stdlib.  Unlike other bits that need extensive metaclass support this is trivial to add:

  class DerivedEnumMeta(EnumMeta):
    def __getitem__(cls, name):
        try:
            return cls._member_map_[name]
        except KeyError:
            result = cls._missing_name_(name)
            if isinstance(result, cls):
                return result
            raise
History
Date User Action Args
2022-04-11 14:58:44adminsetgithub: 73938
2018-01-22 07:47:09ethan.furmansetstatus: open -> closed
versions: - Python 3.6
messages: + msg310401

resolution: rejected
stage: test needed -> resolved
2018-01-18 19:28:25ethan.furmansetmessages: + msg310253
title: Enum._missing_ not called for __getattr__ failures -> Enum._missing_ not called for __getitem__ failures
2017-03-10 01:26:58josh.rsetmessages: + msg289331
2017-03-09 16:47:58ethan.furmansetmessages: + msg289306
2017-03-08 15:31:21josh.rsetnosy: + josh.r
messages: + msg289241
2017-03-07 22:29:52ethan.furmancreate