Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Open sidebar
Dan Gass
plum
Commits
3df43fd7
Commit
3df43fd7
authored
Feb 10, 2020
by
Dan Gass
Browse files
Alpha 14.
parent
b3685332
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
332 additions
and
229 deletions
+332
-229
docs/index.rst
docs/index.rst
+1
-1
docs/quickstart.rst
docs/quickstart.rst
+8
-8
docs/reference/index.rst
docs/reference/index.rst
+1
-1
docs/release_notes.rst
docs/release_notes.rst
+8
-2
docs/tutorials/types/int_bitfields.rst
docs/tutorials/types/int_bitfields.rst
+48
-34
plum-py/plum/int/bitfields/__init__.py
plum-py/plum/int/bitfields/__init__.py
+1
-1
plum-py/plum/int/bitfields/_bitfield.py
plum-py/plum/int/bitfields/_bitfield.py
+63
-57
plum-py/plum/int/bitfields/_bitfields.py
plum-py/plum/int/bitfields/_bitfields.py
+4
-4
plum-py/plum/int/bitfields/_bitfieldstype.py
plum-py/plum/int/bitfields/_bitfieldstype.py
+25
-17
plum-tests/plum_tests/__about__.py
plum-tests/plum_tests/__about__.py
+2
-2
plum-tests/plum_tests/int/bitfields/test_bitfield.py
plum-tests/plum_tests/int/bitfields/test_bitfield.py
+61
-15
plum-tests/plum_tests/int/bitfields/test_exceptions.py
plum-tests/plum_tests/int/bitfields/test_exceptions.py
+55
-0
plum-tests/plum_tests/int/bitfields/test_features.py
plum-tests/plum_tests/int/bitfields/test_features.py
+28
-28
plum-tests/plum_tests/int/bitfields/test_metaclass.py
plum-tests/plum_tests/int/bitfields/test_metaclass.py
+12
-44
plum-tests/plum_tests/int/bitfields/test_nesting.py
plum-tests/plum_tests/int/bitfields/test_nesting.py
+15
-15
No files found.
docs/index.rst
View file @
3df43fd7
...
...
@@ -33,7 +33,7 @@ more complete usage examples. The following summarizes the
:doc:`plum.bytearray </tutorials/types/bytearray>` array of bytes
:doc:`plum.float </tutorials/types/float>` floating point
:doc:`plum.int </tutorials/types/int>` integers
:doc:`plum.int.bitfields </tutorials/types/int_bitfields>` integer with bitfield accessors
:doc:`plum.int.bitfields </tutorials/types/int_bitfields>` integer with bit
field accessors
:doc:`plum.int.enum </tutorials/types/int_enum>` integer enumerated constants
:doc:`plum.int.flag </tutorials/types/int_flag>` integer with bit flags
:doc:`plum.nil </tutorials/types/nil>` no bytes
...
...
docs/quickstart.rst
View file @
3df43fd7
...
...
@@ -190,18 +190,18 @@ provide mechanisms to pack or unpack sequences of bytes and conveniently
specify or access data within the bytes. But |BitFields| types provide
access to individual bits and/or bit fields within the bytes buffer.
The example below demonstrates use of the :
func
:`
b
it
f
ield`
function
The example below demonstrates use of the :
class
:`
B
it
F
ield`
class
to define the bit position and bit field size (in bits) for each
member in the bytes buffer. Notice,
access to the
bitfield
members
produces an
instance of the Python type from the member definition:
member in the bytes buffer. Notice, bit
field
access produces an
instance of the Python type from the member definition:
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>>
>>> class MyBf(BitFields, nbytes=1):
... bflag: bool =
b
it
f
ield(pos=0, size=1)
... iflag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... reserved: int =
b
it
f
ield(pos=6, size=2, default=0)
... bflag: bool =
B
it
F
ield(pos=0, size=1)
... iflag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
... reserved: int =
B
it
F
ield(pos=6, size=2, default=0)
...
>>> # construct instance with keyword arguments,
>>> # fields with default may be omitted
...
...
docs/reference/index.rst
View file @
3df43fd7
...
...
@@ -18,7 +18,7 @@ Plum Types
:mod:`plum.bytearray` array of bytes
:mod:`plum.float` floating point
:mod:`plum.int` integer
:mod:`plum.int.bitfields` integer with bitfield accessors
:mod:`plum.int.bitfields` integer with bit
field accessors
:mod:`plum.int.enum` integer enumerated constants
:mod:`plum.int.flag` integer with bit flags
:mod:`plum.nil` no bytes
...
...
docs/release_notes.rst
View file @
3df43fd7
...
...
@@ -8,6 +8,12 @@ Versions increment per `semver <http://semver.org/>`_ (except for alpha series).
Alpha (0.1.0aX) Versions (only serial number X increments)
**********************************************************
+ 0.1.0a14 (2020-Feb-10)
- Allow :class:`BitFields` subclasses to be further subclassed.
- Eliminate ``bitfield`` function and replace with direct use of :class:`BitField`.
- Accept ``cls`` argument in :class:`BitField` to facilitate type annotation being
different than bit field type.
+ 0.1.0a13 (2020-Feb-9)
- Allow structure subclasses to be further subclassed.
- Require use of :class:`Member()` for all structure member definitions.
...
...
@@ -24,8 +30,8 @@ Alpha (0.1.0aX) Versions (only serial number X increments)
+ 0.1.0a12 (2020-Feb-04)
- Fix memory leaks in plum-boost.
- Fix bitfield representations when embedded within a structure or another
bitfield (previously, they lacked the class name and just showed fields).
- Fix bit
field representations when embedded within a structure or another
bit
field (previously, they lacked the class name and just showed fields).
- Allow __pack__ method to be overridden in structure definitions
(previously metaclass constructed one any that were supplied in class
definition).
...
...
docs/tutorials/types/int_bitfields.rst
View file @
3df43fd7
...
...
@@ -28,16 +28,30 @@ name, followed by a colon and a type (any int-like type), then an equal
sign and finally the position and number of bits of the bit field.
For example:
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>>
>>> class Sample(BitFields, nbytes=1, byteorder='little'):
... boolean_flag: bool =
b
it
f
ield(pos=0, size=1)
... integer_flag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... reserved: int =
b
it
f
ield(pos=6, size=2)
... boolean_flag: bool =
B
it
F
ield(pos=0, size=1)
... integer_flag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
... reserved: int =
B
it
F
ield(pos=6, size=2)
...
>>>
.. Note::
The bit field definition accepts a ``cls`` argument to specify the type.
This facilitates skipping the type annotation or providing a different
type annotation. For example:
>>> class Sample(BitFields, nbytes=1, byteorder='little'):
... boolean_flag = BitField(cls=bool, pos=0, size=1)
... integer_flag = BitField(cls=int, pos=1, size=1)
... nibble = BitField(cls=int, pos=2, size=4)
... reserved = BitField(cls=int, pos=6, size=2)
...
>>>
***************
Unpacking Bytes
...
...
@@ -207,18 +221,18 @@ Defaulting a Bit Field
To specify a default value to be utilized if a bit field value is not provided
during class instantiation (or packing), use the ``default`` argument of the
:
func
:`
b
it
f
ield` definition. For example:
:
class
:`
B
it
F
ield` definition. For example:
.. code-block:: python
:emphasize-lines: 7
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>>
>>> class Sample(BitFields, nbytes=1, byteorder='little'):
... boolean_flag: bool =
b
it
f
ield(pos=0, size=1)
... integer_flag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... reserved: int =
b
it
f
ield(pos=6, size=2, default=3)
... boolean_flag: bool =
B
it
F
ield(pos=0, size=1)
... integer_flag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
... reserved: int =
B
it
F
ield(pos=6, size=2, default=3)
...
>>> # 'reserved' bit field not specified
>>> Sample(boolean_flag=False, integer_flag=0, nibble=0)
...
...
@@ -235,9 +249,9 @@ following example shows the result of a default value of ``0xc0`` (bits 6 and 7
:emphasize-lines: 1
>>> class Sample(BitFields, nbytes=1, byteorder='little', default=0xc0):
... boolean_flag: bool =
b
it
f
ield(pos=0, size=1)
... integer_flag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... boolean_flag: bool =
B
it
F
ield(pos=0, size=1)
... integer_flag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
...
>>> Sample(boolean_flag=False, integer_flag=0, nibble=0).pack()
bytearray(b'\xc0')
...
...
@@ -248,18 +262,18 @@ Ignoring a Bit Field
********************
To specify a bit field to be ignored during comparisons, set the ``ignore`` argument to
``True`` in the :
func
:`
b
it
f
ield` definition. For example:
``True`` in the :
class
:`
B
it
F
ield` definition. For example:
.. code-block:: python
:emphasize-lines: 7
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>>
>>> class Sample(BitFields, nbytes=1, byteorder='little'):
... boolean_flag: bool =
b
it
f
ield(pos=0, size=1)
... integer_flag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... reserved: int =
b
it
f
ield(pos=6, size=2, ignore=True)
... boolean_flag: bool =
B
it
F
ield(pos=0, size=1)
... integer_flag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
... reserved: int =
B
it
F
ield(pos=6, size=2, ignore=True)
...
>>> sample1 = Sample(boolean_flag=False, integer_flag=0, nibble=0, reserved=0)
>>> sample2 = Sample(boolean_flag=False, integer_flag=0, nibble=0, reserved=3)
...
...
@@ -279,9 +293,9 @@ bit mask value of ``0xc0``:
:emphasize-lines: 1
>>> class Sample(BitFields, nbytes=1, byteorder='little', ignore=0xc0):
... boolean_flag: bool =
b
it
f
ield(pos=0, size=1)
... integer_flag: int =
b
it
f
ield(pos=1, size=1)
... nibble: int =
b
it
f
ield(pos=2, size=4)
... boolean_flag: bool =
B
it
F
ield(pos=0, size=1)
... integer_flag: int =
B
it
F
ield(pos=1, size=1)
... nibble: int =
B
it
F
ield(pos=2, size=4)
...
>>> sample1 = Sample(0x00)
>>> sample2 = Sample(0x80)
...
...
@@ -301,7 +315,7 @@ conversion. The most popular choices besides :class:`int`, are
booleans and enumerations as demonstrated in the following example:
>>> from enum import IntFlag
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>>
>>> class Letter(IntFlag): # use IntFlag to tolerate undefined values
...
...
...
@@ -314,8 +328,8 @@ booleans and enumerations as demonstrated in the following example:
...
... """Sample BitFields subclass."""
...
... letter: Letter =
b
it
f
ield(pos=0, size=4)
... boolean: bool =
b
it
f
ield(pos=4, size=1)
... letter: Letter =
B
it
F
ield(pos=0, size=4)
... boolean: bool =
B
it
F
ield(pos=4, size=1)
...
>>> sample = Sample()
>>> sample.boolean
...
...
@@ -338,14 +352,14 @@ Bit field definitions also support nested |plum| bit fields types as
demonstrated next:
>>> class Inner(BitFields, nbytes=1):
... a: int =
b
it
f
ield(pos=0, size=2)
... b: int =
b
it
f
ield(pos=2, size=2)
... a: int =
B
it
F
ield(pos=0, size=2)
... b: int =
B
it
F
ield(pos=2, size=2)
...
>>>
>>> class Outer(BitFields, nbytes=1):
... h: int =
b
it
f
ield(pos=0, size=2)
... i: Inner =
b
it
f
ield(pos=2, size=4)
... j: int =
b
it
f
ield(pos=6, size=2)
... h: int =
B
it
F
ield(pos=0, size=2)
... i: Inner =
B
it
F
ield(pos=2, size=4)
... j: int =
B
it
F
ield(pos=6, size=2)
...
>>> sample = Outer(h=0, i={'a': 1, 'b': 2}, j=3)
>>> sample.i
...
...
@@ -380,9 +394,9 @@ the previous bit field. For example:
>>> class AutoMix(BitFields, nbytes=1):
...
... f1: int =
b
it
f
ield(size=2)
... f2: int =
b
it
f
ield(size=4)
... f3: int =
b
it
f
ield(size=1, pos=7) # explicit position
... f1: int =
B
it
F
ield(size=2)
... f2: int =
B
it
F
ield(size=4)
... f3: int =
B
it
F
ield(size=1, pos=7) # explicit position
...
>>> AutoMix(0x85).dump()
+--------+--------+-------+-------+---------+
...
...
plum-py/plum/int/bitfields/__init__.py
View file @
3df43fd7
...
...
@@ -3,6 +3,6 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Interpret bytes as an unsigned integer with bit fields."""
from
._bitfield
import
b
it
f
ield
from
._bitfield
import
B
it
F
ield
from
._bitfields
import
BitFields
from
._bitfieldstype
import
BitFieldsType
plum-py/plum/int/bitfields/_bitfield.py
View file @
3df43fd7
...
...
@@ -4,17 +4,26 @@
"""Bit field definition."""
from
enum
import
IntEnum
from
importlib
import
import_module
class
BitField
:
"""Bit field definition."""
"""Bit field definition.
:param int size: size in bits
:param int pos: bit offset of least significant bit
:param int default: initial value when unspecified
:param bool signed: interpret as signed integer
:param bool ignore: do not include field in comparisons
"""
# pylint: disable=too-many-instance-attributes
__slots__
=
[
'_make_type'
,
'_
type
'
,
'_
cls
'
,
'mask'
,
'signbit'
,
'default'
,
...
...
@@ -24,10 +33,12 @@ class BitField:
'size'
,
]
def
__init__
(
self
,
pos
,
size
,
default
,
signed
,
ignore
):
def
__init__
(
self
,
*
,
size
,
cls
=
None
,
pos
=
None
,
default
=
None
,
signed
=
False
,
ignore
=
False
):
if
pos
is
not
None
:
pos
=
int
(
pos
)
assert
pos
>=
0
if
pos
<
0
:
raise
TypeError
(
'bit field position must be greater than or equal to zero'
)
size
=
int
(
size
)
signed
=
bool
(
signed
)
...
...
@@ -35,53 +46,70 @@ class BitField:
if
signed
:
if
size
<
2
:
raise
ValueError
(
'signed bitfield must have bit size of 2 or greater'
)
'signed bit
field must have bit size of 2 or greater'
)
signbit
=
1
<<
(
size
-
1
)
else
:
if
size
<
1
:
raise
ValueError
(
'unsigned bitfield must have bit size of 1 or greater'
)
'unsigned bit
field must have bit size of 1 or greater'
)
signbit
=
0
self
.
mask
=
(
1
<<
size
)
-
1
self
.
signbit
=
signbit
self
.
default
=
default
self
.
ignore
=
ignore
self
.
name
=
None
# assigned via __set_name__ protocol
self
.
mask
=
(
1
<<
size
)
-
1
self
.
name
=
None
# filled in later by BitFieldsType
self
.
pos
=
pos
self
.
signbit
=
signbit
self
.
size
=
size
# assigned during BitFields clas
s
c
on
struction (by
#
BitFieldsType
.__new__)
self
.
_
type
=
None
# assigned during BitFields class construction (by
# BitFieldsType.__new__)
self
.
_make_type
=
None
if
cls
i
s
N
on
e
:
# filled in later using type annotation byte
BitFieldsType
self
.
_
cls
=
None
self
.
_make_type
=
None
else
:
self
.
cls
=
cls
@
property
def
type
(
self
):
def
cls
(
self
):
"""Bit field type.
:returns: bit field type
:rtype: type
"""
return
self
.
_
type
return
self
.
_
cls
@
type
.
setter
def
type
(
self
,
cls
):
self
.
_type
=
cls
@
cls
.
setter
def
cls
(
self
,
cls
):
# delay import to avoid circular import issue
bitfields
=
import_module
(
'.bitfields'
,
'plum.int'
)
if
issubclass
(
cls
,
IntEnum
):
self
.
_make_type
=
self
.
_make_enum
elif
issubclass
(
cls
,
int
):
self
.
_make_type
=
self
.
_make_int
else
:
self
.
_make_type
=
self
.
_make_bits
self
.
_cls
=
cls
acceptable
=
True
try
:
if
issubclass
(
cls
,
IntEnum
):
self
.
_make_type
=
self
.
_make_enum
elif
issubclass
(
cls
,
int
):
self
.
_make_type
=
self
.
_make_int
elif
issubclass
(
cls
,
bitfields
.
BitFields
):
self
.
_make_type
=
self
.
_make_bits
else
:
acceptable
=
False
except
TypeError
:
acceptable
=
False
# not a class
if
not
acceptable
:
if
self
.
name
:
raise
TypeError
(
f
'bit field
{
self
.
name
!
r
}
type must be int-like'
)
raise
TypeError
(
f
"bit field type must be int-like"
)
@
property
def
signed
(
self
):
"""Indication if bitfield is a signed integer.
"""Indication if bit
field is a signed integer.
:returns: indication
:rtype: bool
...
...
@@ -92,17 +120,13 @@ class BitField:
def
__repr__
(
self
):
return
(
'BitField('
f
'name=
{
self
.
name
!
r
}
,'
f
'
type
=
{
self
.
_
type
!
r
}
,'
f
'
cls
=
{
self
.
_
cls
!
r
}
,'
f
'pos=
{
self
.
pos
!
r
}
,'
f
'size=
{
self
.
size
!
r
}
,'
f
'default=
{
self
.
default
!
r
}
,'
f
'signed=
{
self
.
signed
!
r
}
'
')'
)
def
__set_name__
(
self
,
owner
,
name
):
assert
self
.
name
is
None
,
'bitfield definition already in use'
self
.
name
=
name
def
__get__
(
self
,
obj
,
objtype
=
None
):
if
obj
is
None
:
return
self
...
...
@@ -116,7 +140,7 @@ class BitField:
return
self
.
_make_type
(
obj
,
value
)
def
_make_bits
(
self
,
obj
,
value
):
item
=
self
.
_
type
(
value
)
item
=
self
.
_
cls
(
value
)
try
:
bitoffset
,
store
=
obj
.
__bitoffset_store__
except
AttributeError
:
...
...
@@ -128,13 +152,13 @@ class BitField:
def
_make_int
(
self
,
obj
,
value
):
# pylint: disable=unused-argument
return
self
.
_
type
(
value
)
return
self
.
_
cls
(
value
)
def
_make_enum
(
self
,
obj
,
value
):
# pylint: disable=unused-argument
try
:
# pylint: disable=not-callable
value
=
self
.
type
(
value
)
value
=
self
.
cls
(
value
)
except
ValueError
:
# must not be a part of the enumeration
pass
...
...
@@ -149,8 +173,8 @@ class BitField:
try
:
value
=
int
(
value
)
except
TypeError
:
# must be a nested bitfield (where value could be a dict)
value
=
self
.
_
type
(
value
)
# must be a nested bit
field (where value could be a dict)
value
=
self
.
_
cls
(
value
)
if
self
.
signbit
:
minvalue
=
-
(
1
<<
(
size
-
1
))
...
...
@@ -161,7 +185,7 @@ class BitField:
if
(
value
<
minvalue
)
or
(
value
>
maxvalue
):
raise
ValueError
(
f
'bitfield
{
self
.
name
!
r
}
requires
{
minvalue
}
<= number <=
{
maxvalue
}
'
)
f
'bit
field
{
self
.
name
!
r
}
requires
{
minvalue
}
<= number <=
{
maxvalue
}
'
)
try
:
bitoffset
,
obj
=
obj
.
__bitoffset_store__
...
...
@@ -171,21 +195,3 @@ class BitField:
pos
+=
bitoffset
obj
.
__value__
=
(
obj
.
__value__
&
~
(
mask
<<
pos
))
|
((
value
&
mask
)
<<
pos
)
# FUTURE: look at dataclass implementation as to why this should be a function
# rather than just leaving user instantiate class directly
# (has something to do with IDE introspection/hints)
def
bitfield
(
*
,
size
,
pos
=
None
,
default
=
None
,
signed
=
False
,
ignore
=
False
):
"""Create bit field definition.
:param int size: size in bits
:param int pos: bit offset of least significant bit
:param int default: initial value when unspecified
:param bool signed: interpret as signed integer
:param bool ignore: do not include field in comparisons
:returns: bit field definition
:rtype: BitField
"""
return
BitField
(
pos
,
size
,
default
,
signed
,
ignore
)
plum-py/plum/int/bitfields/_bitfields.py
View file @
3df43fd7
...
...
@@ -132,16 +132,16 @@ class BitFields(Plum, metaclass=BitFieldsType, nbytes=4, byteorder='little', def
if
not
isinstance
(
value
,
cls
):
value
=
cls
(
value
)
for
name
,
field
in
cls
.
__fields__
.
items
():
if
issubclass
(
field
.
type
,
BitFields
):
row
=
dump
.
add_record
(
access
=
'.'
+
name
,
cls
=
field
.
type
)
field
.
type
.
__add_bitfields_to_dump__
(
if
issubclass
(
field
.
cls
,
BitFields
):
row
=
dump
.
add_record
(
access
=
'.'
+
name
,
cls
=
field
.
cls
)
field
.
cls
.
__add_bitfields_to_dump__
(
getattr
(
cls
,
name
).
__get__
(
value
,
cls
),
row
,
bitoffset
+
field
.
pos
)
else
:
dump
.
add_record
(
access
=
'.'
+
name
,
bits
=
(
bitoffset
+
field
.
pos
,
field
.
size
),
value
=
str
(
getattr
(
cls
,
name
).
__get__
(
value
,
cls
)),
cls
=
field
.
type
)
cls
=
field
.
cls
)
def
__repr__
(
self
):
# str( ) around getattr formats enumerations correctly (otherwise shows
...
...
plum-py/plum/int/bitfields/_bitfieldstype.py
View file @
3df43fd7
...
...
@@ -3,8 +3,6 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""BitFields type metaclass."""
from
enum
import
IntEnum
from
..._plumtype
import
PlumType
from
._bitfield
import
BitField
...
...
@@ -33,10 +31,10 @@ class BitFieldsType(PlumType):
For example:
>>> from plum.int.bitfields import BitFields,
b
it
f
ield
>>> from plum.int.bitfields import BitFields,
B
it
F
ield
>>> class MyBits(BitFields, nbytes=1, byteorder='big', default=0, ignore=0x80):
... nibble: int =
b
it
f
ield(pos=0, size=4)
... threebits: int =
b
it
f
ield(pos=4, size=3)
... nibble: int =
B
it
F
ield(pos=0, size=4)
... threebits: int =
B
it
F
ield(pos=4, size=3)
...
>>>
...
...
@@ -55,20 +53,30 @@ class BitFieldsType(PlumType):
# validate bit field class attributes
fields
=
{}
for
fieldname
,
typ
in
getattr
(
cls
,
'__annotations__'
,
{}).
items
():
field
=
getattr
(
cls
,
fieldname
,
None
)
annotations
=
getattr
(
cls
,
'__annotations__'
,
{})
if
not
isinstance
(
field
,
BitField
):
raise
TypeError
(
f
'bit field
{
fieldname
!
r
}
must be assigned a bitfield()'
)
fields
=
{}
for
fieldname
,
bitfield
in
namespace
.
items
():
if
not
isinstance
(
bitfield
,
BitField
):
continue
if
not
(
isinstance
(
typ
,
BitFieldsType
)
or
issubclass
(
typ
,
(
int
,
IntEnum
))):
if
bitfield
.
name
is
None
:
bitfield
.
name
=
fieldname
else
:
raise
TypeError
(
f
'bit field
{
fieldname
!
r
}
type must be int-like'
)
f
"invalid bit field
{
fieldname
!
r
}
definition, "
f
"
{
type
(
bitfield
).
__name__
}
() instance can not be shared "
f
"between bit fields classes"
)
if
bitfield
.
cls
is
None
:
try
:
bitfield
.
cls
=
annotations
[
fieldname
]
except
KeyError
:
raise
TypeError
(
f
"bit field
{
fieldname
!
r
}
must be assigned a type using either "
f
"the BitField() 'cls' argument or a type annotation"
)
field
.
type
=
typ
fields
[
fieldname
]
=
field
fields
[
fieldname
]
=
bitfield
# fill in missing positions
...
...
@@ -145,8 +153,8 @@ class BitFieldsType(PlumType):
for
field
in
fields
.
values
():
if
field
.
ignore
:
ignore
|=
field
.
mask
<<
field
.
pos
elif
isinstance
(
field
.
type
,
BitFieldsType
):
ignore
|=
field
.
type
.
__ignore__
<<
field
.
pos
elif
isinstance
(
field
.
cls
,
BitFieldsType
):
ignore
|=
field
.
cls
.
__ignore__
<<
field
.
pos
cls
.
__byteorder__
=
byteorder
cls
.
__compare_mask__
=
(
max_
^
ignore
)
&
max_
...
...
plum-tests/plum_tests/__about__.py
View file @
3df43fd7
...
...
@@ -36,7 +36,7 @@ class VersionInfo:
major
=
0
minor
=
1
micro
=
0
serial
=
1
serial
=
1
4
version
=
f
'
{
major
}
.
{
minor
}
.
{
micro
}
'
...
...
@@ -60,7 +60,7 @@ __version__ = VersionInfo.version
__author__
=
'Dan Gass'
__maintainer__
=
'Dan Gass'
__email__
=
'dan.gass@gmail.com'
__copyright__
=
'Copyright 20
19
Daniel Mark Gass'
__copyright__
=
'Copyright 20
20
Daniel Mark Gass'
__license__
=
'MIT License; http://opensource.org/licenses/MIT'
...
...
plum-tests/plum_tests/int/bitfields/test_bitfield.py
View file @
3df43fd7
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Copyright 2019 Daniel Mark Gass, see __about__.py for license information.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Test basic features of
b
it
f
ield member class."""
"""Test basic features of
B
it
F
ield member class."""
import
pytest
from
baseline
import
Baseline
from
plum.int.bitfields
import
b
it
f
ield
from
plum.int.bitfields
import
B
it
F
ield
from
...utils
import
wrap_message
...
...
@@ -18,15 +18,61 @@ class TestFeatures:
def
test_repr
(
self
):
"""Verify repr() behavior."""
expected
=
Baseline
(
"""
BitField(name=None,
type
=None,pos=0,size=1,default=None,signed=False)
BitField(name=None,
cls
=None,pos=0,size=1,default=None,signed=False)
"""
)
assert
repr
(
b
it
f
ield
(
pos
=
0
,
size
=
1
))
==
expected
assert
repr
(
B
it
F
ield
(
pos
=
0
,
size
=
1
))
==
expected
def
test_signed
(
self
):
"""Verify signed property behavior."""
assert
bitfield
(
pos
=
0
,
size
=
2
,
signed
=
True
).
signed