Completed
Push — master ( 887f61...192352 )
by Aaron
06:16
created

BitField.pack()   C

Complexity

Conditions 7

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
c 1
b 0
f 0
dl 0
loc 34
rs 5.5
1
import re
2
3
4
class BitField(object):
5
    def __init__(self, enum):
6
        if not all(isinstance(member.value, int) for member in enum):
7
            msg = 'Enum {} members must have integer values'.format(repr(enum))
8
            raise TypeError(msg)
9
        try:
10
            assert enum(0)
11
            msg = 'Cannot construct BitField from {} with a value for 0: {}'
12
            raise TypeError(msg.format(repr(enum), enum(0)))
13
        except ValueError:
14
            # A ValueError is raised if the enum does not have a value for 0
15
            pass
16
        self.enum = enum
17
18
    def __repr__(self):
19
        return 'BitField({})'.format(self.enum)
20
21
    def __str__(self):
22
        return 'BitField({})'.format(self.enum)
23
24
    def pack(self, arg):
25
        """
26
        Take a list (or single value) and bitwise-or all the values together
27
        """
28
        if arg:
29
            # Handle a variety of inputs: list or single, enum or raw
30
            if isinstance(arg, list):
31
                arg_list = arg
32
            else:
33
                arg_list = [arg]
34
35
            # To make usage a bit nice/easier if the elements of the list
36
            # are strings assume that they are enum names and attempt to
37
            # convert them to the correct enumeration values.
38
            value = 0
39
            for item in arg_list:
40
                if isinstance(item, self.enum):
41
                    value |= item.value
42
                elif isinstance(item, str):
43
                    try:
44
                        value |= getattr(self.enum, item).value
45
                    except AttributeError:
46
                        enum_name = re.match(r"<enum '(\S+)'>", str(self.enum)).group(1)
47
                        msg = '{} is not a valid {}'.format(item, enum_name)
48
                        raise ValueError(msg)
49
                else:
50
                    # Assume that the item is an integer value, convert it to
51
                    # an enum value to ensure it is a valid value for this
52
                    # bitfield.
53
                    value |= self.enum(item).value
54
55
            return value
56
        else:
57
            return 0
58
59
    def unpack(self, val):
60
        """
61
        Take a single number and split it out into all values that are present
62
        """
63
        return frozenset(e for e in self.enum if e.value & val)
64
65
    def make(self, arg):
66
        """
67
        Take an input list and return a frozenset
68
69
        useful for testing
70
        """
71
        # Handle the same inputs as the pack function
72
        if isinstance(arg, list):
73
            values = [self.enum(value) for value in arg]
74
        else:
75
            values = [self.enum(arg)]
76
77
        # return this list as a frozenset
78
        return frozenset(values)
79