@@ -84,6 +84,9 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
84
84
It is an abstract base class; :class:`StructLayout`, :class:`UnionLayout`,
85
85
:class:`ArrayLayout`, and :class:`FlexibleLayout` implement concrete layout rules.
86
86
New layout rules can be defined by inheriting from this class.
87
+
88
+ Like all other shape-castable objects, all layouts are immutable. New classes deriving from
89
+ :class:`Layout` must preserve this invariant.
87
90
"""
88
91
89
92
@staticmethod
@@ -274,14 +277,6 @@ class StructLayout(Layout):
274
277
"""
275
278
276
279
def __init__ (self , members ):
277
- self .members = members
278
-
279
- @property
280
- def members (self ):
281
- return {key : field .shape for key , field in self ._fields .items ()}
282
-
283
- @members .setter
284
- def members (self , members ):
285
280
offset = 0
286
281
self ._fields = {}
287
282
if not isinstance (members , Mapping ):
@@ -300,6 +295,10 @@ def members(self, members):
300
295
self ._fields [key ] = Field (shape , offset )
301
296
offset += cast_shape .width
302
297
298
+ @property
299
+ def members (self ):
300
+ return {key : field .shape for key , field in self ._fields .items ()}
301
+
303
302
def __iter__ (self ):
304
303
return iter (self ._fields .items ())
305
304
@@ -348,14 +347,6 @@ class UnionLayout(Layout):
348
347
Dictionary of union members.
349
348
"""
350
349
def __init__ (self , members ):
351
- self .members = members
352
-
353
- @property
354
- def members (self ):
355
- return {key : field .shape for key , field in self ._fields .items ()}
356
-
357
- @members .setter
358
- def members (self , members ):
359
350
self ._fields = {}
360
351
if not isinstance (members , Mapping ):
361
352
raise TypeError ("Union layout members must be provided as a mapping, not {!r}"
@@ -372,6 +363,10 @@ def members(self, members):
372
363
.format (shape )) from e
373
364
self ._fields [key ] = Field (shape , 0 )
374
365
366
+ @property
367
+ def members (self ):
368
+ return {key : field .shape for key , field in self ._fields .items ()}
369
+
375
370
def __iter__ (self ):
376
371
return iter (self ._fields .items ())
377
372
@@ -429,34 +424,26 @@ class ArrayLayout(Layout):
429
424
Amount of elements.
430
425
"""
431
426
def __init__ (self , elem_shape , length ):
432
- self .elem_shape = elem_shape
433
- self .length = length
434
-
435
- @property
436
- def elem_shape (self ):
437
- return self ._elem_shape
438
-
439
- @elem_shape .setter
440
- def elem_shape (self , elem_shape ):
441
427
try :
442
428
Shape .cast (elem_shape )
443
429
except TypeError as e :
444
430
raise TypeError ("Array layout element shape must be a shape-castable object, "
445
431
"not {!r}"
446
432
.format (elem_shape )) from e
433
+ if not isinstance (length , int ) or length < 0 :
434
+ raise TypeError ("Array layout length must be a non-negative integer, not {!r}"
435
+ .format (length ))
447
436
self ._elem_shape = elem_shape
437
+ self ._length = length
438
+
439
+ @property
440
+ def elem_shape (self ):
441
+ return self ._elem_shape
448
442
449
443
@property
450
444
def length (self ):
451
445
return self ._length
452
446
453
- @length .setter
454
- def length (self , length ):
455
- if not isinstance (length , int ) or length < 0 :
456
- raise TypeError ("Array layout length must be a non-negative integer, not {!r}"
457
- .format (length ))
458
- self ._length = length
459
-
460
447
def __iter__ (self ):
461
448
offset = 0
462
449
for index in range (self ._length ):
@@ -519,39 +506,14 @@ class FlexibleLayout(Layout):
519
506
Fields defined in the layout.
520
507
"""
521
508
def __init__ (self , size , fields ):
522
- self .size = size
523
- self .fields = fields
524
-
525
- @property
526
- def size (self ):
527
- """:meta private:""" # work around Sphinx bug
528
- return self ._size
529
-
530
- @size .setter
531
- def size (self , size ):
532
509
if not isinstance (size , int ) or size < 0 :
533
510
raise TypeError ("Flexible layout size must be a non-negative integer, not {!r}"
534
511
.format (size ))
535
- if hasattr (self , "_fields" ) and self ._fields :
536
- endmost_name , endmost_field = max (self ._fields .items (),
537
- key = lambda pair : pair [1 ].offset + pair [1 ].width )
538
- if endmost_field .offset + endmost_field .width > size :
539
- raise ValueError ("Flexible layout size {} does not cover the field '{}', which "
540
- "ends at bit {}"
541
- .format (size , endmost_name ,
542
- endmost_field .offset + endmost_field .width ))
543
- self ._size = size
544
-
545
- @property
546
- def fields (self ):
547
- return {** self ._fields }
548
-
549
- @fields .setter
550
- def fields (self , fields ):
551
- self ._fields = {}
552
512
if not isinstance (fields , Mapping ):
553
513
raise TypeError ("Flexible layout fields must be provided as a mapping, not {!r}"
554
514
.format (fields ))
515
+ self ._size = size
516
+ self ._fields = {}
555
517
for key , field in fields .items ():
556
518
if not isinstance (key , (int , str )) or (isinstance (key , int ) and key < 0 ):
557
519
raise TypeError ("Flexible layout field name must be a non-negative integer or "
@@ -560,12 +522,21 @@ def fields(self, fields):
560
522
if not isinstance (field , Field ):
561
523
raise TypeError ("Flexible layout field value must be a Field instance, not {!r}"
562
524
.format (field ))
563
- if field .offset + field .width > self . _size :
525
+ if field .offset + field .width > size :
564
526
raise ValueError ("Flexible layout field '{}' ends at bit {}, exceeding "
565
527
"the size of {} bit(s)"
566
- .format (key , field .offset + field .width , self . _size ))
528
+ .format (key , field .offset + field .width , size ))
567
529
self ._fields [key ] = field
568
530
531
+ @property
532
+ def size (self ):
533
+ """:meta private:""" # work around Sphinx bug
534
+ return self ._size
535
+
536
+ @property
537
+ def fields (self ):
538
+ return {** self ._fields }
539
+
569
540
def __iter__ (self ):
570
541
return iter (self ._fields .items ())
571
542
0 commit comments