Completed
Push — master ( 4fcd67...5cc95b )
by Aaron
9s
created

Element.factory()   D

Complexity

Conditions 10

Size

Total Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 11.5625

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 10
c 5
b 0
f 0
dl 0
loc 71
ccs 15
cts 20
cp 0.75
crap 11.5625
rs 4.186

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like Element.factory() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""StarStruct element class."""
2
3 1
import starstruct
4
5
6 1
def register(cls):
7
    """ A handy decorator to register a class as an element """
8 1
    Element.register(cls)
9 1
    return cls
10
11
12 1
class Element(object):
13
    """
14
    A class factory that determines the type of the field passed in, and
15
    instantiates the correct class type.
16
    """
17 1
    elementtypes = []
18
19 1
    @classmethod
20
    def register(cls, element):
21
        """Function used to register new element subclasses."""
22 1
        cls.elementtypes.append(element)
23
24 1
    @classmethod
25 1
    def factory(cls, field, mode=starstruct.modes.Mode.Native, alignment=1):
26
        """
27
        Initialize a StarStruct element object based on the type of element
28
        parameters provided.
29
30
        The field must be a tuple of the following form:
31
            (name, format, <optional>)
32
33
        Where the values in the tuple determine the type of element.
34
35
        These are the possible element types:
36
         1. Normal (base): a standard python struct format character, and a
37
            field name are provided.  The optional element should not provided.
38
39
         2. Enum: a standard python struct format character and field name are
40
            provided, but the 3rd optional element is provided which is a
41
            subclass of enum.Enum.
42
43
         3. Length: a standard python struct format character that represents
44
            an unsigned numeric value, and the field name are provided, but the
45
            3rd optional element is provided and is a string.  In this case the
46
            string is assumed to be another field which is the name of a
47
            Variable element.
48
49
         4. Variable: a variable length element that accommodates 0 or more of
50
            another StarStruct.message.  The format field should be a valid
51
            StarStruct.message, the optional 3rd element must be provided and
52
            should be the name of a valid Length element or an int.  The
53
            validity of the referenced element must be checked after the
54
            creation of the entire message with the Message.validate() function.
55
56
         5. Discriminated: a message element that can have multiple formats
57
            such as a C union.  The format field should be a dictionary where
58
            the keys represent values of a referenced enumeration field, and
59
            the value for each entry is a valid StarStruct.message, or None.
60
            The optional 3rd element must be provided and should be the name of
61
            a valid Enum element.  The validity of the referenced element must
62
            be checked after the creation of the entire message with the
63
            Message.validate() function.
64
        """
65
66 1
        if not isinstance(mode, starstruct.modes.Mode):
67
            raise TypeError('invalid mode: {}'.format(mode))
68
69
        # The field parameter is a single field tuple:
70
        #   ('name', 'format', <optional>)
71 1
        if not isinstance(field, tuple):
72
            raise TypeError('invalid element: {}'.format(field))
73
74
        # The name of the element must be a non-null string or bytes
75
        # provided in as the first part of the field tuple
76 1
        if not field[0] or not isinstance(field[0], (str, bytes)):
77
            raise TypeError('invalid name: {}'.format(field[0]))
78
79 1
        valid_elems = []
80 1
        for elem in cls.elementtypes:
81 1
            try:
82 1
                if elem.valid(field):
83 1
                    valid_elems.append(elem)
84 1
            except (TypeError, KeyError):
85 1
                continue
86
87 1
        if len(valid_elems) > 1:
88
            raise ValueError('More than one elemn was valid.\n\tField: {0}\n\tElems: {1}'.format(
89
                field, valid_elems))
90 1
        elif len(valid_elems) == 1:
91 1
            return valid_elems[0](field, mode, alignment)
92
93
        # If the function made it this far, the field specification is not valid
94
        raise TypeError('invalid field: {}'.format(field))
95
96 1
    @staticmethod
97
    def valid(field):
98
        """Require element objects to implement this function."""
99
        raise NotImplementedError
100
101 1
    def validate(self, msg):
102
        """Require element objects to implement this function."""
103
        raise NotImplementedError
104
105 1
    def update(self, mode, alignment):
106
        """Require element objects to implement this function."""
107
        raise NotImplementedError
108
109 1
    def pack(self, msg):
110
        """Require element objects to implement this function."""
111
        raise NotImplementedError
112
113 1
    def unpack(self, msg, buf):
114
        """Require element objects to implement this function."""
115
        raise NotImplementedError
116
117 1
    def make(self, msg):
118
        """Require element objects to implement this function."""
119
        raise NotImplementedError
120