pyof.foundation.basic_types.IPv6Address.unpack()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 25
Code Lines 10

Duplication

Lines 25
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 2.0438

Importance

Changes 0
Metric Value
eloc 10
dl 25
loc 25
ccs 7
cts 9
cp 0.7778
rs 9.9
c 0
b 0
f 0
cc 2
nop 3
crap 2.0438
1
"""Basic types used in structures and messages."""
2
3
# System imports
4 1
import struct
5 1
from copy import deepcopy
6
7
# Local source tree imports
8 1
from pyof.foundation import exceptions
9 1
from pyof.foundation.base import GenericStruct, GenericType, UBIntBase
10
11 1
__all__ = ('BinaryData', 'Char', 'ConstantTypeList', 'FixedTypeList',
12
           'IPAddress', 'DPID', 'HWAddress', 'Pad', 'UBInt8', 'UBInt16',
13
           'UBInt32', 'UBInt64', 'UBInt128')
14
15
16 1
class Pad(GenericType):
17
    """Class for padding attributes."""
18
19 1
    _fmt = ''
20
21 1
    def __init__(self, length=0):
22
        """Pad up to ``length``, in bytes.
23
24
        Args:
25
            length (int): Total length, in bytes.
26
        """
27 1
        super().__init__()
28 1
        self._length = length
29
30 1
    def __repr__(self):
31
        return "{}({})".format(type(self).__name__, self._length)
32
33 1
    def __str__(self):
34
        return '0' * self._length
35
36 1
    def get_size(self, value=None):
37
        """Return the type size in bytes.
38
39
        Args:
40
            value (int): In structs, the user can assign other value instead of
41
                this class' instance. Here, in such cases, ``self`` is a class
42
                attribute of the struct.
43
44
        Returns:
45
            int: Size in bytes.
46
47
        """
48 1
        return self._length
49
50 1
    def unpack(self, buff, offset=0):
51
        """Unpack *buff* into this object.
52
53
        Do nothing, since the _length is already defined and it is just a Pad.
54
        Keep buff and offset just for compability with other unpack methods.
55
56
        Args:
57
            buff: Buffer where data is located.
58
            offset (int): Where data stream begins.
59
        """
60
61 1
    def pack(self, value=None):
62
        """Pack the object.
63
64
        Args:
65
            value (int): In structs, the user can assign other value instead of
66
                this class' instance. Here, in such cases, ``self`` is a class
67
                attribute of the struct.
68
69
        Returns:
70
            bytes: the byte 0 (zero) *length* times.
71
72
        """
73 1
        return b'\x00' * self._length
74
75 1
    def __deepcopy__(self, memo):
76
        """Improve deepcopy speed."""
77 1
        return Pad(length=self._length)
78
79
80 1
class UBInt8(UBIntBase):
81
    """Format character for an Unsigned Char.
82
83
    Class for an 8-bit (1-byte) Unsigned Integer.
84
    """
85
86 1
    _fmt = "!B"
87
88
89 1
class UBInt16(UBIntBase):
90
    """Format character for an Unsigned Short.
91
92
    Class for an 16-bit (2-byte) Unsigned Integer.
93
    """
94
95 1
    _fmt = "!H"
96
97
98 1
class UBInt32(UBIntBase):
99
    """Format character for an Unsigned Int.
100
101
    Class for an 32-bit (4-byte) Unsigned Integer.
102
    """
103
104 1
    _fmt = "!I"
105
106
107 1
class UBInt64(UBIntBase):
108
    """Format character for an Unsigned Long Long.
109
110
    Class for an 64-bit (8-byte) Unsigned Integer.
111
    """
112
113 1
    _fmt = "!Q"
114
115
116 1
class UBInt128(UBIntBase):
117
    """Format character for an Unsigned Long Long.
118
119
    Class for an 128-bit (16-byte) Unsigned Integer.
120
    """
121
122 1
    _fmt = "!8H"
123
124
125 1
class DPID(GenericType):
126
    """DataPath ID. Identifies a switch."""
127
128 1
    _fmt = "!8B"
129
130 1
    def __init__(self, dpid=None):
131
        """Create an instance and optionally set its dpid value.
132
133
        Args:
134
            dpid (str): String with DPID value(e.g. `00:00:00:00:00:00:00:01`).
135
        """
136 1
        super().__init__(value=dpid)
137
138 1
    def __str__(self):
139
        return self._value
140
141 1
    @property
142
    def value(self):
143
        """Return dpid value.
144
145
        Returns:
146
            str: DataPath ID stored by DPID class.
147
148
        """
149
        return self._value
150
151 1
    def pack(self, value=None):
152
        """Pack the value as a binary representation.
153
154
        Returns:
155
            bytes: The binary representation.
156
157
        Raises:
158
            struct.error: If the value does not fit the binary format.
159
160
        """
161 1
        if isinstance(value, type(self)):
162 1
            return value.pack()
163 1
        if value is None:
164 1
            value = self._value
165 1
        return struct.pack('!8B', *[int(v, 16) for v in value.split(':')])
166
167 1
    def unpack(self, buff, offset=0):
168
        """Unpack a binary message into this object's attributes.
169
170
        Unpack the binary value *buff* and update this object attributes based
171
        on the results.
172
173
        Args:
174
            buff (bytes): Binary data package to be unpacked.
175
            offset (int): Where to begin unpacking.
176
177
        Raises:
178
            Exception: If there is a struct unpacking error.
179
180
        """
181 1
        begin = offset
182 1
        hexas = []
183 1
        while begin < offset + 8:
184 1
            number = struct.unpack("!B", buff[begin:begin+1])[0]
185 1
            hexas.append("%.2x" % number)
186 1
            begin += 1
187 1
        self._value = ':'.join(hexas)
188
189 1
    def __deepcopy__(self, memo):
190
        """Improve deepcopy speed."""
191 1
        return DPID(dpid=self._value)
192
193
194 1
class Char(GenericType):
195
    """Build a double char type according to the length."""
196
197 1
    def __init__(self, value=None, length=0):
198
        """Create a Char with the optional parameters below.
199
200
        Args:
201
            value: The character to be build.
202
            length (int): Character size.
203
        """
204 1
        super().__init__(value)
205 1
        self.length = length
206 1
        self._fmt = '!{}{}'.format(self.length, 's')
207
208 1
    def pack(self, value=None):
209
        """Pack the value as a binary representation.
210
211
        Returns:
212
            bytes: The binary representation.
213
214
        Raises:
215
            struct.error: If the value does not fit the binary format.
216
217
        """
218 1
        if isinstance(value, type(self)):
219 1
            return value.pack()
220
221 1
        try:
222 1
            if value is None:
223 1
                value = self.value
224 1
            packed = struct.pack(self._fmt, bytes(value, 'ascii'))
225 1
            return packed[:-1] + b'\0'  # null-terminated
226
        except struct.error as err:
227
            msg = "Char Pack error. "
228
            msg += "Class: {}, struct error: {} ".format(type(value).__name__,
229
                                                         err)
230
            raise exceptions.PackException(msg)
231
232 1
    def unpack(self, buff, offset=0):
233
        """Unpack a binary message into this object's attributes.
234
235
        Unpack the binary value *buff* and update this object attributes based
236
        on the results.
237
238
        Args:
239
            buff (bytes): Binary data package to be unpacked.
240
            offset (int): Where to begin unpacking.
241
242
        Raises:
243
            Exception: If there is a struct unpacking error.
244
245
        """
246 1
        try:
247 1
            begin = offset
248 1
            end = begin + self.length
249 1
            unpacked_data = struct.unpack(self._fmt, buff[begin:end])[0]
250
        except struct.error:
251
            raise Exception("%s: %s" % (offset, buff))
252
253 1
        self._value = unpacked_data.decode('ascii').rstrip('\0')
254
255 1
    def __deepcopy__(self, memo):
256
        """Improve deepcopy speed."""
257 1
        return Char(value=self._value, length=self.length)
258
259
260 1
class IPAddress(GenericType):
261
    """Defines a IP address."""
262
263 1
    netmask = UBInt32()
264 1
    max_prefix = UBInt32(32)
265
266 1
    def __init__(self, address="0.0.0.0/32", netmask=None):
267
        """Create an IPAddress with the parameters below.
268
269
        Args:
270
            address (str): IP Address using ipv4. Defaults to '0.0.0.0/32'
271
        """
272 1
        if '/' in address:
273 1
            address, netmask = address.split('/')
274
        else:
275 1
            netmask = 32 if netmask is None else netmask
276
277 1
        super().__init__(address)
278 1
        self.netmask = int(netmask)
279
280 1 View Code Duplication
    def pack(self, value=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
281
        """Pack the value as a binary representation.
282
283
        If the value is None the self._value will be used to pack.
284
285
        Args:
286
            value (str): IP Address with ipv4 format.
287
288
        Returns:
289
            bytes: The binary representation.
290
291
        Raises:
292
            struct.error: If the value does not fit the binary format.
293
294
        """
295 1
        if isinstance(value, type(self)):
296 1
            return value.pack()
297
298 1
        if value is None:
299 1
            value = self._value
300
301 1
        if value.find('/') >= 0:
302
            value = value.split('/')[0]
303
304 1
        try:
305 1
            value = value.split('.')
306 1
            return struct.pack('!4B', *[int(x) for x in value])
307
        except struct.error as err:
308
            msg = "IPAddress error. "
309
            msg += "Class: {}, struct error: {} ".format(type(value).__name__,
310
                                                         err)
311
            raise exceptions.PackException(msg)
312
313 1
    def unpack(self, buff, offset=0):
314
        """Unpack a binary message into this object's attributes.
315
316
        Unpack the binary value *buff* and update this object attributes based
317
        on the results.
318
319
        Args:
320
            buff (bytes): Binary data package to be unpacked.
321
            offset (int): Where to begin unpacking.
322
323
        Raises:
324
            Exception: If there is a struct unpacking error.
325
326
        """
327 1
        try:
328 1
            unpacked_data = struct.unpack('!4B', buff[offset:offset+4])
329 1
            self._value = '.'.join([str(x) for x in unpacked_data])
330
        except struct.error as exception:
331
            raise exceptions.UnpackException('%s; %s: %s' % (exception,
332
                                                             offset, buff))
333
334 1
    def get_size(self, value=None):
335
        """Return the ip address size in bytes.
336
337
        Args:
338
            value: In structs, the user can assign other value instead of
339
                this class' instance. Here, in such cases, ``self`` is a class
340
                attribute of the struct.
341
342
        Returns:
343
            int: The address size in bytes.
344
345
        """
346 1
        return 4
347
348 1
    def __deepcopy__(self, memo):
349
        """Improve deepcopy speed."""
350 1
        return IPAddress(address=self._value, netmask=self.netmask)
351
352
353 1
class IPv6Address(GenericType):
354
    """Defines a IPv6 address."""
355
356 1
    netmask = UBInt128()
357
358 1
    def __init__(self, address="0000:0000:0000:0000:0000:0000:0000:0000/128",
359
                 netmask=None):
360
        """Create an IPv6Address with the parameters below.
361
362
        Args:
363
            address (str): IP Address using IPv6.
364
            Defaults to '0000:0000:0000:0000:0000:0000:0000:0000/128'
365
        """
366 1
        if '/' in address:
367 1
            address, netmask = address.split('/')
368
        else:
369 1
            netmask = 128 if netmask is None else netmask
370
371 1
        if address == '::':
372
            address = '0:0:0:0:0:0:0:0'
373 1
        elif '::' in address:
374 1
            temp = address.split(':')
375 1
            index = temp.index('')
376 1
            temp = [x for x in temp if x != '']
377 1
            address = temp[:index] + ['0'] * (8 - len(temp)) + temp[index:]
378 1
            address = ':'.join(address)
379
380 1
        super().__init__(address)
381 1
        self.netmask = int(netmask)
382
383 1 View Code Duplication
    def pack(self, value=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
384
        """Pack the value as a binary representation.
385
386
        If the value is None the self._value will be used to pack.
387
388
        Args:
389
            value (str): IP Address with IPv6 format.
390
391
        Returns:
392
            bytes: The binary representation.
393
394
        Raises:
395
            struct.error: If the value does not fit the binary format.
396
397
        """
398 1
        if isinstance(value, type(self)):
399 1
            return value.pack()
400
401 1
        if value is None:
402 1
            value = self._value
403
404 1
        if value.find('/') >= 0:
405
            value = value.split('/')[0]
406
407 1
        try:
408 1
            value = value.split(':')
409 1
            return struct.pack('!8H', *[int(x, 16) for x in value])
410
        except struct.error as err:
411
            msg = "IPv6Address error. "
412
            msg += "Class: {}, struct error: {} ".format(type(value).__name__,
413
                                                         err)
414
            raise exceptions.PackException(msg)
415
416 1 View Code Duplication
    def unpack(self, buff, offset=0):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
417
        """Unpack a binary message into this object's attributes.
418
419
        Unpack the binary value *buff* and update this object attributes based
420
        on the results.
421
422
        Args:
423
            buff (bytes): Binary data package to be unpacked.
424
            offset (int): Where to begin unpacking.
425
426
        Raises:
427
            Exception: If there is a struct unpacking error.
428
429
        """
430 1
        def _int2hex(number):
431 1
            return "{0:0{1}x}".format(number, 4)
432
433 1
        try:
434 1
            unpacked_data = struct.unpack('!8H', buff[offset:offset+16])
435
        except struct.error as exception:
436
            raise exceptions.UnpackException('%s; %s: %s' % (exception,
437
                                                             offset, buff))
438
439 1
        transformed_data = ':'.join([_int2hex(x) for x in unpacked_data])
440 1
        self._value = transformed_data
441
442 1
    def get_size(self, value=None):
443
        """Return the IPv6 address size in bytes.
444
445
        Args:
446
            value: In structs, the user can assign other value instead of
447
                this class' instance. Here, in such cases, ``self`` is a class
448
                attribute of the struct.
449
450
        Returns:
451
            int: The address size in bytes.
452
453
        """
454 1
        return 16
455
456 1
    def __deepcopy__(self, memo):
457
        """Improve deepcopy speed."""
458 1
        return IPv6Address(address=self._value, netmask=self.netmask)
459
460
461 1
class HWAddress(GenericType):
462
    """Defines a hardware address."""
463
464
    # pylint: disable=useless-super-delegation
465 1
    def __init__(self, hw_address='00:00:00:00:00:00'):
466
        """Create a HWAddress with the parameters below.
467
468
        Args:
469
            hw_address (bytes): Hardware address. Defaults to
470
                '00:00:00:00:00:00'.
471
        """
472 1
        super().__init__(hw_address)
473
474 1 View Code Duplication
    def pack(self, value=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
475
        """Pack the value as a binary representation.
476
477
        If the passed value (or the self._value) is zero (int), then the pack
478
        will assume that the value to be packed is '00:00:00:00:00:00'.
479
480
        Returns
481
            bytes: The binary representation.
482
483
        Raises:
484
            struct.error: If the value does not fit the binary format.
485
486
        """
487 1
        if isinstance(value, type(self)):
488 1
            return value.pack()
489
490 1
        if value is None:
491 1
            value = self._value
492
493 1
        if value == 0:
494
            value = '00:00:00:00:00:00'
495
496 1
        value = value.split(':')
497
498 1
        try:
499 1
            return struct.pack('!6B', *[int(x, 16) for x in value])
500
        except struct.error as err:
501
            msg = "HWAddress error. "
502
            msg += "Class: {}, struct error: {} ".format(type(value).__name__,
503
                                                         err)
504
            raise exceptions.PackException(msg)
505
506 1 View Code Duplication
    def unpack(self, buff, offset=0):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
507
        """Unpack a binary message into this object's attributes.
508
509
        Unpack the binary value *buff* and update this object attributes based
510
        on the results.
511
512
        Args:
513
            buff (bytes): Binary data package to be unpacked.
514
            offset (int): Where to begin unpacking.
515
516
        Raises:
517
            Exception: If there is a struct unpacking error.
518
519
        """
520 1
        def _int2hex(number):
521 1
            return "{0:0{1}x}".format(number, 2)
522
523 1
        try:
524 1
            unpacked_data = struct.unpack('!6B', buff[offset:offset+6])
525
        except struct.error as exception:
526
            raise exceptions.UnpackException('%s; %s: %s' % (exception,
527
                                                             offset, buff))
528
529 1
        transformed_data = ':'.join([_int2hex(x) for x in unpacked_data])
530 1
        self._value = transformed_data
531
532 1
    def get_size(self, value=None):
533
        """Return the address size in bytes.
534
535
        Args:
536
            value: In structs, the user can assign other value instead of
537
                this class' instance. Here, in such cases, ``self`` is a class
538
                attribute of the struct.
539
540
        Returns:
541
            int: The address size in bytes.
542
543
        """
544 1
        return 6
545
546 1
    def is_broadcast(self):
547
        """Return true if the value is a broadcast address. False otherwise."""
548
        return self.value == 'ff:ff:ff:ff:ff:ff'
549
550 1
    def __deepcopy__(self, memo):
551
        """Improve deepcopy speed."""
552 1
        return HWAddress(hw_address=self._value)
553
554
555 1
class BinaryData(GenericType):
556
    """Class to create objects that represent binary data.
557
558
    This is used in the ``data`` attribute from
559
    :class:`~pyof.v0x01.asynchronous.packet_in.PacketIn` and
560
    :class:`~pyof.v0x01.controller2switch.packet_out.PacketOut` messages.
561
    Both the :meth:`pack` and :meth:`unpack` methods will return the
562
    binary data itself. :meth:`get_size` method will
563
    return the size of the instance using Python's :func:`len`.
564
    """
565
566 1
    def __init__(self, value=None):  # pylint: disable=useless-super-delegation
567
        """Initialize with a value (optional).
568
569
        Args:
570
            value (bytes): The binary data. Defaults to an empty value.
571
        """
572 1
        super().__init__(value)
573
574 1
    def pack(self, value=None):
575
        """Pack the value as a binary representation.
576
577
        Returns:
578
            bytes: The binary representation.
579
580
        Raises:
581
            ValueError: If value can't be represented with bytes
582
583
        """
584 1
        if value is None:
585 1
            value = self._value
586
587 1
        if hasattr(value, 'pack') and callable(value.pack):
588 1
            return value.pack()
589 1
        if isinstance(value, bytes):
590 1
            return value
591 1
        if value is None:
592 1
            return b''
593 1
        raise ValueError(f"BinaryData can't be {type(value)} = '{value}'")
594
595 1
    def unpack(self, buff, offset=0):
596
        """Unpack a binary message into this object's attributes.
597
598
        Unpack the binary value *buff* and update this object attributes based
599
        on the results. Since the *buff* is binary data, no conversion is done.
600
601
        Args:
602
            buff (bytes): Binary data package to be unpacked.
603
            offset (int): Where to begin unpacking.
604
        """
605 1
        self._value = buff[offset:]
606
607 1
    def get_size(self, value=None):
608
        """Return the size in bytes.
609
610
        Args:
611
            value (bytes): In structs, the user can assign other value instead
612
                of this class' instance. Here, in such cases, ``self`` is a
613
                class attribute of the struct.
614
615
        Returns:
616
            int: The address size in bytes.
617
618
        """
619 1
        if value is None:
620 1
            value = self._value
621
622 1
        if hasattr(value, 'get_size'):
623 1
            return value.get_size()
624
625 1
        return len(self.pack(value))
626
627 1
    def __deepcopy__(self, memo):
628
        """Improve deepcopy speed."""
629 1
        return BinaryData(value=self._value)
630
631
632 1
class TypeList(list, GenericStruct):
633
    """Base class for lists that store objects of one single type."""
634
635 1
    def __init__(self, items):
636
        """Initialize the list with one item or a list of items.
637
638
        Args:
639
            items (iterable, ``pyof_class``): Items to be stored.
640
        """
641 1
        super().__init__()
642 1
        if isinstance(items, list):
643 1
            self.extend(items)
644 1
        elif items:
645
            self.append(items)
646
647 1
    def extend(self, items):
648
        """Extend the list by adding all items of ``items``.
649
650
        Args:
651
            items (iterable): Items to be added to the list.
652
653
        Raises:
654
            :exc:`~.exceptions.WrongListItemType`: If an item has an unexpected
655
                type.
656
657
        """
658 1
        for item in items:
659 1
            self.append(item)
660
661 1
    def pack(self, value=None):
662
        """Pack the value as a binary representation.
663
664
        Returns:
665
            bytes: The binary representation.
666
667
        """
668 1
        if isinstance(value, type(self)):
669 1
            return value.pack()
670
671 1
        if value is None:
672 1
            value = self
673
        else:
674 1
            container = type(self)(items=None)
675 1
            container.extend(value)
676 1
            value = container
677
678 1
        bin_message = b''
679 1
        try:
680 1
            for item in value:
681 1
                bin_message += item.pack()
682 1
            return bin_message
683
        except exceptions.PackException as err:
684
            msg = "{} pack error: {}".format(type(self).__name__, err)
685
            raise exceptions.PackException(msg)
686
687
    # pylint: disable=arguments-differ
688 1
    def unpack(self, buff, item_class, offset=0):
689
        """Unpack the elements of the list.
690
691
        Args:
692
            buff (bytes): The binary data to be unpacked.
693
            item_class (:obj:`type`): Class of the expected items on this list.
694
            offset (int): If we need to shift the beginning of the data.
695
        """
696 1
        begin = offset
697 1
        limit_buff = len(buff)
698
699 1
        while begin < limit_buff:
700 1
            item = item_class()
701 1
            item.unpack(buff, begin)
702 1
            self.append(item)
703 1
            begin += item.get_size()
704
    # pylint: enable=arguments-differ
705
706 1
    def get_size(self, value=None):
707
        """Return the size in bytes.
708
709
        Args:
710
            value: In structs, the user can assign other value instead of
711
                this class' instance. Here, in such cases, ``self`` is a class
712
                attribute of the struct.
713
714
        Returns:
715
            int: The size in bytes.
716
717
        """
718 1
        if value is None:
719 1
            if not self:
720
                # If this is a empty list, then returns zero
721 1
                return 0
722 1
            if issubclass(type(self[0]), GenericType):
723
                # If the type of the elements is GenericType, then returns the
724
                # length of the list multiplied by the size of the GenericType.
725
                return len(self) * self[0].get_size()
726
727
            # Otherwise iter over the list accumulating the sizes.
728 1
            return sum(item.get_size() for item in self)
729
730 1
        return type(self)(value).get_size()
731
732 1
    def __str__(self):
733
        """Human-readable object representantion."""
734
        return "{}".format([str(item) for item in self])
735
736 1
    def __deepcopy__(self, memo):
737
        """Improve deepcopy speed."""
738
        items = [deepcopy(item) for item in self]
739
        return TypeList(items=items)
740
741
742 1
class FixedTypeList(TypeList):
743
    """A list that stores instances of one pyof class."""
744
745 1
    _pyof_class = None
746
747 1
    def __init__(self, pyof_class, items=None):
748
        """Create a FixedTypeList with the parameters follows.
749
750
        Args:
751
            pyof_class (:obj:`type`): Class of the items to be stored.
752
            items (iterable, ``pyof_class``): Items to be stored.
753
        """
754 1
        self._pyof_class = pyof_class
755 1
        super().__init__(items)
756
757 1
    def append(self, item):
758
        """Append one item to the list.
759
760
        Args:
761
            item: Item to be appended. Its type must match the one defined in
762
                the constructor.
763
764
        Raises:
765
            :exc:`~.exceptions.WrongListItemType`: If the item has a different
766
                type than the one specified in the constructor.
767
768
        """
769 1
        if isinstance(item, list):
770
            self.extend(item)
771 1
        elif issubclass(item.__class__, self._pyof_class):
772 1
            list.append(self, item)
773
        else:
774
            raise exceptions.WrongListItemType(item.__class__.__name__,
775
                                               self._pyof_class.__name__)
776
777 1
    def insert(self, index, item):
778
        """Insert an item at the specified index.
779
780
        Args:
781
            index (int): Position to insert the item.
782
            item: Item to be inserted. It must have the type specified in the
783
                constructor.
784
785
        Raises:
786
            :exc:`~.exceptions.WrongListItemType`: If the item has a different
787
                type than the one specified in the constructor.
788
789
        """
790
        if issubclass(item.__class__, self._pyof_class):
791
            list.insert(self, index, item)
792
        else:
793
            raise exceptions.WrongListItemType(item.__class__.__name__,
794
                                               self._pyof_class.__name__)
795
796 1
    def unpack(self, buff, offset=0):  # pylint: disable=arguments-differ
797
        """Unpack the elements of the list.
798
799
        This unpack method considers that all elements have the same size.
800
        To use this class with a pyof_class that accepts elements with
801
        different sizes, you must reimplement the unpack method.
802
803
        Args:
804
            buff (bytes): The binary data to be unpacked.
805
            offset (int): If we need to shift the beginning of the data.
806
        """
807 1
        super().unpack(buff, self._pyof_class, offset)
808
809 1
    def __deepcopy__(self, memo):
810
        """Improve deepcopy speed."""
811 1
        items = [deepcopy(item) for item in self]
812 1
        return FixedTypeList(pyof_class=self._pyof_class, items=items)
813
814
815 1
class ConstantTypeList(TypeList):
816
    """List that contains only objects of the same type (class).
817
818
    The types of all items are expected to be the same as the first item's.
819
    Otherwise, :exc:`~.exceptions.WrongListItemType` is raised in many
820
    list operations.
821
    """
822
823
    # pylint: disable=useless-super-delegation
824 1
    def __init__(self, items=None):
825
        """Create a ConstantTypeList that can contain itens to be stored.
826
827
        Args:
828
            items (iterable, :class:`object`): Items to be stored.
829
830
        Raises:
831
            :exc:`~.exceptions.WrongListItemType`: If an item has a different
832
                type than the first item to be stored.
833
834
        """
835
        super().__init__(items)
836
837 1
    def append(self, item):
838
        """Append one item to the list.
839
840
        Args:
841
            item: Item to be appended.
842
843
        Raises:
844
            :exc:`~.exceptions.WrongListItemType`: If an item has a different
845
                type than the first item to be stored.
846
847
        """
848
        if isinstance(item, list):
849
            self.extend(item)
850
        elif not self:
851
            list.append(self, item)
852
        elif item.__class__ == self[0].__class__:
853
            list.append(self, item)
854
        else:
855
            raise exceptions.WrongListItemType(item.__class__.__name__,
856
                                               self[0].__class__.__name__)
857
858 1
    def insert(self, index, item):
859
        """Insert an item at the specified index.
860
861
        Args:
862
            index (int): Position to insert the item.
863
            item: Item to be inserted.
864
865
        Raises:
866
            :exc:`~.exceptions.WrongListItemType`: If an item has a different
867
                type than the first item to be stored.
868
869
        """
870
        if not self:
871
            list.append(self, item)
872
        elif item.__class__ == self[0].__class__:
873
            list.insert(self, index, item)
874
        else:
875
            raise exceptions.WrongListItemType(item.__class__.__name__,
876
                                               self[0].__class__.__name__)
877
878 1
    def __deepcopy__(self, memo):
879
        """Improve deepcopy speed."""
880
        items = [deepcopy(item) for item in self]
881
        return ConstantTypeList(items=items)
882