Issues (43)

starstruct/element.py (1 issue)

1
"""StarStruct element class."""
2
3 1
from typing import Optional, Tuple
0 ignored issues
show
The import typing could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
4
5 1
from starstruct.modes import Mode
6
7
8 1
def register(cls):
9
    """ A handy decorator to register a class as an element """
10 1
    Element.register(cls)
11 1
    return cls
12
13
14 1
class Element(object):
15
    """
16
    A class factory that determines the type of the field passed in, and
17
    instantiates the correct class type.
18
    """
19 1
    elementtypes = []
20
21 1
    @classmethod
22
    def register(cls, element):
23
        """Function used to register new element subclasses."""
24 1
        cls.elementtypes.append(element)
25
26 1
    @classmethod
27 1
    def factory(cls, field: tuple, mode: Optional[Mode]=Mode.Native, alignment: Optional[int]=1):
28
        """
29
        Initialize a StarStruct element object based on the type of element
30
        parameters provided.
31
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
        :param field: The field must be a tuple of the following form::
67
68
            (name, format, <optional>)
69
70
        :param mode: The mode in which to pack the information.
71
        :param alignment: The number of bytes to align objects with.
72
        :returns: An element whose fields match those passed in
73
        """
74
75 1
        if not isinstance(mode, Mode):
76
            raise TypeError('invalid mode: {}'.format(mode))
77
78
        # The field parameter is a single field tuple:
79
        #   ('name', 'format', <optional>)
80 1
        if not isinstance(field, tuple):
81
            raise TypeError('invalid element: {}'.format(field))
82
83
        # The name of the element must be a non-null string or bytes
84
        # provided in as the first part of the field tuple
85 1
        if not field[0] or not isinstance(field[0], (str, bytes)):
86
            raise TypeError('invalid name: {}'.format(field[0]))
87
88 1
        valid_elems = []
89 1
        for elem in cls.elementtypes:
90 1
            try:
91 1
                if elem.valid(field):
92 1
                    valid_elems.append(elem)
93 1
            except (TypeError, KeyError):
94 1
                continue
95
96 1
        if len(valid_elems) > 1:
97
            raise ValueError('More than one elemn was valid.\n\tField: {0}\n\tElems: {1}'.format(
98
                field, valid_elems))
99 1
        elif len(valid_elems) == 1:
100 1
            return valid_elems[0](field, mode, alignment)
101
102
        # If the function made it this far, the field specification is not valid
103
        raise TypeError('invalid field: {}'.format(field))
104
105 1
    @staticmethod
106 1
    def valid(field: tuple) -> bool:
107
        """
108
        Require element objects to implement this abstract function.
109
110
        Validation function to determine if a field tuple represents a valid
111
        element type.
112
113
        The basics have already been validated by the Element factory class,
114
        validate that the struct format is a valid numeric value.
115
116
        :param field: The format specifier for an element
117
        :returns: Whether this field tuple is valid for this class.
118
        """
119
        raise NotImplementedError
120
121 1
    def validate(self, msg: dict) -> bool:
122
        """
123
        Require element objects to implement this function.
124
125
        :param msg: The current values passed in to the element
126
        :returns: Whether this message represents a valid element.
127
        """
128
        raise NotImplementedError
129
130 1
    def update(self, mode: Mode, alignment: int) -> None:
131
        """
132
        Require element objects to implement this function.
133
134
        :param mode: The new mode for the Element
135
        :param alignment: The new alignment for the element
136
        """
137
        raise NotImplementedError
138
139 1
    def pack(self, msg: dict) -> bytes:
140
        """
141
        Require element objects to implement this function.
142
143
        :param msg: The values to pack into bytes
144
        :returns: The msg packed into bytes as specified by the format
145
        """
146
        raise NotImplementedError
147
148 1
    def unpack(self, msg: dict, buf: bytes) -> Tuple[dict, bytes]:
149
        """
150
        Require element objects to implement this function.
151
152
        :param msg: The values unpacked thus far from the bytes
153
        :param buf: The remaining bytes to unpack
154
        :returns: The updated message and the remaining bytes
155
        """
156
        raise NotImplementedError
157
158 1
    def make(self, msg: dict):
159
        """
160
        Require element objects to implement this function.
161
162
        :param msg: The values to place into the named tuple object
163
164
        :todo: How do I specify the correct type for this?
165
        """
166
        raise NotImplementedError
167