BitField.make()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 15.2377

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
c 2
b 1
f 0
dl 0
loc 19
ccs 1
cts 9
cp 0.1111
crap 15.2377
rs 9.2
1 1
import re
2 1
import functools
3
4
5 1
class BitField(object):
6 1
    def __init__(self, enum):
7 1
        if not all(isinstance(member.value, int) for member in enum):
8 1
            msg = 'Enum {} members must have integer values'.format(repr(enum))
9 1
            raise TypeError(msg)
10 1
        try:
11 1
            assert enum(0)
12 1
            msg = 'Cannot construct BitField from {} with a value for 0: {}'
13 1
            raise TypeError(msg.format(repr(enum), enum(0)))
14 1
        except ValueError:
15
            # A ValueError is raised if the enum does not have a value for 0
16 1
            pass
17 1
        self.enum = enum
18
19
        # Determine the bit mask and length for this bitfield
20 1
        self.bit_mask = functools.reduce(lambda x, y: x | y, [e.value for e in self.enum])
21 1
        self.bit_length = self.bit_mask.bit_length()
22
23 1
    def __repr__(self):
24 1
        return 'BitField({})'.format(self.enum)
25
26 1
    def __str__(self):
27
        return 'BitField({})'.format(self.enum)
28
29 1
    def find_value(self, item):
30
        """
31
        Take a value and determine the enumeration value based on value or
32
        enum memeber name.
33
        """
34
        # pylint: disable=too-many-branches
35 1
        if isinstance(item, str):
36
            # To make usage a bit nice/easier if the elements of the list are
37
            # strings assume that they are enum names and attempt to convert
38
            # them to the correct enumeration values.
39 1
            try:
40 1
                value = getattr(self.enum, item)
41 1
            except AttributeError:
42
                # This is the normal error to throw if the enum name is
43
                # not valid for this enumeration type.
44 1
                enum_name = re.match(r"<enum '(\S+)'>", str(self.enum)).group(1)
45 1
                msg = '{} is not a valid {}'.format(item, enum_name)
46 1
                raise ValueError(msg)
47 1
        elif isinstance(item, self.enum):
48 1
            value = item
49
        else:
50
            # Assume that the item is an integer value, convert it to an enum
51
            # value to ensure it is a valid value for this bitfield
52 1
            try:
53 1
                value = self.enum(item)
54 1
            except ValueError:
55
                # This value is not a valid enumeration value
56 1
                enum_name = re.match(r"<enum '(\S+)'>", str(self.enum)).group(1)
57 1
                msg = '{} is not a valid {}'.format(item, enum_name)
58 1
                raise ValueError(msg)
59
60 1
        return value
61
62 1
    def pack(self, arg):
63
        """
64
        Take a list (or single value) and bitwise-or all the values together
65
        """
66 1
        value = 0
67 1
        if arg is not None:
68
            # Handle a variety of inputs: list or single, enum or raw
69 1
            if hasattr(arg, '__iter__'):
70 1
                arg_list = arg
71
            else:
72 1
                arg_list = [arg]
73
74 1
            for item in arg_list:
75 1
                value |= self.find_value(item).value
76
77 1
        return value
78
79 1
    def unpack(self, val):
80
        """
81
        Take a single number and split it out into all values that are present
82
        """
83 1
        return frozenset(e for e in self.enum if e.value & val)
84
85 1
    def make(self, arg):
86
        """
87
        Take an input list and return a frozenset
88
89
        useful for testing
90
        """
91
        values = []
92
        if arg is not None:
93
            # Handle a variety of inputs: list or single, enum or raw
94
            if hasattr(arg, '__iter__'):
95
                arg_list = arg
96
            else:
97
                arg_list = [arg]
98
99
            for item in arg_list:
100
                values.append(self.find_value(item))
101
102
        # return this list as a frozenset
103
        return frozenset(values)
104