1

I am wondering if I can use python enums to enforce specific string inputs from a user into my functions and classes, and avoid having to code the explicit checks against acceptable values.

So instead of this:

e.g.

utilities_supplied = {
    'electricity': 'Yes',
    'gas': 'No',
    'water': 'Yes',
}

def find_utility(utility):
    try:
        print(utilities_supplied[utility])
    except KeyError:
        raise KeyError(f'Please choose one of {utilities_supplied.keys()}, \'{utility}\' provided')

I would like to do this:


from enum import Enum
class Utility(Enum):
    electricity = 1
    gas = 2
    water = 3

Utility['electric']  
# not a member, I want this to raise an error which lists the acceptable options.

Is it possible for enums to raise an error listing the possible enumeration members?

2
  • 1
    Can you clarify what you need? enums already "enforce specific string inputs". Your example code does raise an error. Commented Feb 24, 2020 at 11:36
  • I would like the error raised to also list the acceptable string inputs, so the user can adjust their string input to match one of the enum members. Commented Feb 24, 2020 at 11:42

2 Answers 2

2

You can use EnumMeta to overwrite __getitem__, which fetches the values from the enum:

import enum


class MyEnum(enum.EnumMeta):
    def __getitem__(cls, name):
        try:
            return super().__getitem__(name)
        except (KeyError) as error:
            options = ', '.join(cls._member_map_.keys())
            msg = f'Please choose one of {options}, \'{name}\' provided'
            raise ValueError(msg) from None


class Utility(enum.Enum, metaclass=MyEnum):
    electricity = 1
    gas = 2
    water = 3


fire = Utility['fire']
print(fire)

Output:

ValueError: Please choose one of electricity, gas, water, 'fire' provided
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I had tried overriding getattr on the base Enum class but didn't get the desired result. This looks like a good solution.
@MauriceMeyer, could you also show a solution using __missing__, and place it first? Subclassing EnumMeta as you have done works, but it's easy to get wrong. See this question for details.
Would be interested to see an implementation that avoids subclassing if possible.
1

EDIT: This doesn't really work when I subclass the ValidatedEnum class. Any help gratefully received.

The solution using _missing_ was fairly straightforward it turns out! Thanks for the idea @EthanFurman.

from enum import Enum
class ValidatedEnum(Enum):
    electricity = 1
    gas = 2
    water = 3

    @classmethod
    def _missing_(cls, value):
        choices = list(cls.__members__.keys())
        raise ValueError("%r is not a valid %s, please choose from %s" % (value, cls.__name__, choices))

<ipython-input-1-8b49d805ac2d> in _missing_(cls, value)
      8     def _missing_(cls, value):
      9         choices = list(cls.__members__.keys())
---> 10         raise ValueError("%r is not a valid %s, please choose from %s" % (value, cls.__name__, choices))
     11 

ValueError: 'electric' is not a valid ValidatedEnum, please choose from ['electricity', 'gas', 'water']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.