Search code examples
pythonenumsbitmask

Enum for bitwise operations with user readable strings table


I am looking for an efficient and maintainable way to create a table in Python that can be used to look up user readable strings for enumeration values.

Constraints:

  • I want it to work with an enumeration that supports bitwise operations. For example: passing in a value of enumeration values that has been bitmasked together will return a list of strings for each bitmasked value.
  • I want the user readable strings to be translated from the enumeration value names so I don't have to maintain a table that has to be updated every time the enumeration is modified.
  • I want it to be efficient. For example, I don't want a static function that will do the conversion every time it's called. I want to create a static table that is initialized once with the strings. For Example, I want to create a static dict that looks like this: {Privileges.CanAddPost: "can add post", Privileges.CanDeletePost: "can delete post", ...}
from enum import IntFlag, unique

@unique
class Privileges(IntFlag):
    """Privileges enum that supports bitwise operations"""
    NoPrivileges = 0
    CanAddPost = 1
    CanDeletePost = 2
    CanBanUser = 4
    CanResetPasswords = 8
    CanModerateDiscussions = 16
    CanSuspendAccounts = 32
    All = CanAddPost | CanDeletePost | CanBanUser |\
        CanResetPasswords | CanModerateDiscussions | CanSuspendAccounts

#Instantiate the static variable
Privileges.strings_map = ...  # How do initialize this table?

Solution

  • To accomplish this I created two functions. The first is a static function _toString(privilege) that will convert a enumeration value name to a user readable string. For example: CanAddPost becomes "can add post"

    The 2nd function list_privileges() converts a bitmask of enumeration values to a list of strings. For Example: Privileges.CanAddPost|Privileges.CanDeletePost becomes ["can add post", "can delete post"]

    I use a dictionary comprehension to create a static string_map lookup table.

    from enum import IntFlag, unique
    
    @unique
    class Privileges(IntFlag):
        """Privileges enum that supports bitwise operations"""
        NoPrivileges = 0
        CanAddPost = 1
        CanDeletePost = 2
        CanBanUser = 4
        CanResetPasswords = 8
        CanModerateDiscussions = 16
        CanSuspendAccounts = 32
        All = CanAddPost | CanDeletePost | CanBanUser |\
            CanResetPasswords | CanModerateDiscussions | CanSuspendAccounts
    
        @staticmethod
        def _toString(privilege) -> str:
            """Converts a Privileges enum value to a string"""
            return "".join([char if char.islower() else " " + char.lower()\
                            for char in privilege.name]).lstrip()
    
        def list_privileges(self) -> list:
            """Converts a bitmask of Privileges to a list of readable strings"""
            return list(\
                    [Privileges.strings_map[privilege] for privilege in Privileges\
                        if privilege != Privileges.NoPrivileges\
                            and privilege != Privileges.All\
                            and (privilege & self) == privilege])
    
    # Create static strings_map that will map a Privilege enum value to a 
    #   readable string
    Privileges.strings_map = { privilege: Privileges._toString(privilege) for privilege in Privileges}
    
    user_privileges = Privileges.CanAddPost | Privileges.CanDeletePost
    admin_privileges = Privileges.All
    
    print(user_privileges.list_privileges())
    print(admin_privileges.list_privileges())
    

    Output:

    ['can add post', 'can delete post']
    ['can add post', 'can delete post', 'can ban user', 'can reset passwords', 'can moderate discussions', 'can suspend accounts']