Enumerations

I recently learned about Enums. Here's a fine ChatGPT example which is essentially the content of of Google search result. ChatGPT is like the old internet where you could read text on websites.

ChatGPT

Following is just verbatim output from the following request:

➜  ~ llm "give me an example of using Enums in python"

Sure! Here's an example of using Enums in Python:

from enum import Enum

# Define an Enum class
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

# Accessing Enum members
print(Color.RED)                    # Color.RED
print(Color.RED.name)               # RED
print(Color.RED.value)              # 1

# Iterating over Enum members
for color in Color:
    print(color)

# Checking Enum membership
print(Color.RED in Color)           # True
print(Color.YELLOW in Color)        # False

# Comparing Enum members
print(Color.RED == Color.RED)       # True
print(Color.RED == Color.GREEN)     # False

# Enum as dictionary keys
colors = {
    Color.RED: "FF0000",
    Color.GREEN: "00FF00",
    Color.BLUE: "0000FF"
}
print(colors[Color.RED])            # FF0000

In this example, we define an enum class called "Color" with three members - RED, GREEN, and BLUE. We can access enum members using dot notation (e.g., Color.RED). We can also access the name and value of an enum member using the name and value properties.

We can iterate over all the enum members using a for loop. Enum membership can be checked using the in keyword.

Enum members can be compared using the equality operator (==). We can also use enums as dictionary keys, as shown in the last example.

Back to me

This is all great. My takeaway is that we can replace strings with Enums across the codebase. The Color class enumerates color options and removes typos. There are two more things I want, however.

First, since this is an API interacting with other services, we need to be able to serialize to JSON.

print(json.dumps({"inspection": Inspection.VISUAL}))
# TypeError: Object of type Inspection is not JSON serializable

print(json.dumps({"inspection": Inspection.VISUAL.value}))
{"inspection": "Visual Check"}

The default Enu, as you can see, is not serializable – but is value is. So all over the codebase we’d have to call .value whenever the Enum gets turned into JSON and that's not cool.

Well, can chatGPT help here? Maybe but in the time it'd take to fiddle with GPT I can do a Google search and get my answer on StackOverflow. The trick is to inherit from str and Enum.

class Inspection(str, Enum):
   """JSON serializable inspection types with custom exception"""


   VISUAL = "Visual Check"
   BASIC = "Basic Inspection"
   STANDARD = "Standard Inspection"
   ADVANCED = "Advanced Inspection"


print(json.dumps({"inspection": Inspection.VISUAL}))
# {"inspection": "Visual Check"}

Boom! First problems solved and now the second problem.... A nice property of Enums is, like a dictionary, they raise an error if you try to make an Enum from an unknown value. For Example:

print(Inspection("Just look it it real quick"))
# ValueError: 'Just look it it real quick' is not a valid Inspection

This is nice but we want to raise a custom exception (whether we should have custom exceptions is a topic for another day)

def test_unknown_inspection():
   with pytest.raises(MissingModelConfigError):
       Inspection("Give it a quick look")

No problem because as we can see in stacktrace

   575     @classmethod
    576     def _missing_(cls, value):
--> 577         raise ValueError("%r is not a valid %s" % (value, cls.__name__))

all we have to do is override the _missing_ method.

Result

Now we're done

class Inspection(str, Enum):
   """JSON serializable inspection types with custom exception"""


   VISUAL = "Visual Check"
   BASIC = "Basic Inspection"
   STANDARD = "Standard Inspection"
   ADVANCED = "Advanced Inspection"


   @classmethod
   def _missing_(cls, value):
       raise MissingModelConfigError(
           missing_property="inspection", expected_value=value
       )

For this effort we don’t have to worry about typos or an unknown inspection sneaking in. All we need to care about is a subset of these defined inspections. Very cool!

links

social