pystructtype 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pystructtype-0.2.0 → pystructtype-0.4.0}/.github/workflows/build.yml +4 -4
- {pystructtype-0.2.0 → pystructtype-0.4.0}/.github/workflows/release.yml +1 -1
- {pystructtype-0.2.0 → pystructtype-0.4.0}/.gitignore +4 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/PKG-INFO +41 -16
- {pystructtype-0.2.0 → pystructtype-0.4.0}/README.md +38 -12
- {pystructtype-0.2.0 → pystructtype-0.4.0}/docs/conf.py +3 -1
- {pystructtype-0.2.0 → pystructtype-0.4.0}/pyproject.toml +40 -27
- {pystructtype-0.2.0 → pystructtype-0.4.0}/src/pystructtype/__init__.py +4 -4
- pystructtype-0.4.0/src/pystructtype/bitstype.py +86 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/src/pystructtype/structdataclass.py +103 -146
- {pystructtype-0.2.0 → pystructtype-0.4.0}/src/pystructtype/structtypes.py +65 -15
- {pystructtype-0.2.0 → pystructtype-0.4.0}/src/pystructtype/utils.py +8 -4
- {pystructtype-0.2.0 → pystructtype-0.4.0}/test/examples.py +14 -13
- pystructtype-0.4.0/test/test_bitstype.py +92 -0
- pystructtype-0.4.0/test/test_examples.py +9 -0
- pystructtype-0.4.0/test/test_structdataclass_additional.py +425 -0
- pystructtype-0.4.0/test/test_structtypes.py +150 -0
- pystructtype-0.4.0/test/test_utils.py +31 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/tox.ini +2 -2
- pystructtype-0.4.0/uv.lock +797 -0
- pystructtype-0.2.0/.python-version +0 -1
- pystructtype-0.2.0/setup.cfg +0 -6
- pystructtype-0.2.0/src/pystructtype/bitstype.py +0 -142
- pystructtype-0.2.0/test/test_ctypes.py +0 -50
- pystructtype-0.2.0/uv.lock +0 -655
- {pystructtype-0.2.0 → pystructtype-0.4.0}/.readthedocs.yaml +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/LICENSE.txt +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/docs/autoapi_templates/python/module.rst_t +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/docs/autoapi_templates/python/package.rst_t +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/docs/index.rst +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/src/pystructtype/py.typed +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/test/__init__.py +0 -0
- {pystructtype-0.2.0 → pystructtype-0.4.0}/test/py.typed +0 -0
|
@@ -14,13 +14,13 @@ jobs:
|
|
|
14
14
|
- name: Set up Python
|
|
15
15
|
uses: actions/setup-python@v5
|
|
16
16
|
with:
|
|
17
|
-
python-version: 3.
|
|
17
|
+
python-version: 3.14
|
|
18
18
|
|
|
19
19
|
- name: Run Tests
|
|
20
20
|
run: |
|
|
21
21
|
uv sync
|
|
22
22
|
uv tool install tox
|
|
23
|
-
tox -e
|
|
23
|
+
tox -e py314
|
|
24
24
|
check:
|
|
25
25
|
runs-on: ubuntu-latest
|
|
26
26
|
steps:
|
|
@@ -33,7 +33,7 @@ jobs:
|
|
|
33
33
|
- name: Set up Python
|
|
34
34
|
uses: actions/setup-python@v5
|
|
35
35
|
with:
|
|
36
|
-
python-version: 3.
|
|
36
|
+
python-version: 3.14
|
|
37
37
|
|
|
38
38
|
- name: Check Formatting
|
|
39
39
|
run: |
|
|
@@ -53,7 +53,7 @@ jobs:
|
|
|
53
53
|
- name: Set up Python
|
|
54
54
|
uses: actions/setup-python@v5
|
|
55
55
|
with:
|
|
56
|
-
python-version: 3.
|
|
56
|
+
python-version: 3.14
|
|
57
57
|
|
|
58
58
|
- name: Check Formatting
|
|
59
59
|
run: |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pystructtype
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Leverage Python Types to Define C-Struct Interfaces
|
|
5
5
|
Project-URL: Homepage, https://github.com/fchorney/pystructtype
|
|
6
6
|
Project-URL: Documentation, https://pystructtype.readthedocs.io/en/latest/
|
|
@@ -15,10 +15,9 @@ Classifier: Intended Audience :: Developers
|
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Classifier: Natural Language :: English
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Topic :: Utilities
|
|
20
|
-
Requires-Python:
|
|
21
|
-
Requires-Dist: loguru>=0.7.3
|
|
20
|
+
Requires-Python: <4.0,>=3.14
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
|
|
24
23
|
# PyStructTypes
|
|
@@ -57,7 +56,6 @@ struct MyStruct {
|
|
|
57
56
|
```
|
|
58
57
|
|
|
59
58
|
```python
|
|
60
|
-
@struct_dataclass
|
|
61
59
|
class MyStruct(StructDataclass):
|
|
62
60
|
myNum: int16_t
|
|
63
61
|
myLetter: char_t
|
|
@@ -84,7 +82,6 @@ struct MyStruct {
|
|
|
84
82
|
};
|
|
85
83
|
```
|
|
86
84
|
```python
|
|
87
|
-
@struct_dataclass
|
|
88
85
|
class MyStruct(StructDataclass):
|
|
89
86
|
myInts: Annotated[list[uint8_t], TypeMeta(size=4)]
|
|
90
87
|
myBiggerInts: Annotated[list[uint16_t], TypeMeta(size=2)]
|
|
@@ -110,7 +107,6 @@ struct MyStruct {
|
|
|
110
107
|
```
|
|
111
108
|
|
|
112
109
|
```python
|
|
113
|
-
@struct_dataclass
|
|
114
110
|
class MyStruct(StructDataclass):
|
|
115
111
|
myInt: uint8_t = 5
|
|
116
112
|
myInts: Annnotated[list[uint8_t], TypeMeta(size=2, default=1)]
|
|
@@ -121,6 +117,39 @@ s.decode([10, 5, 6])
|
|
|
121
117
|
# MyStruct(myInt=10, myInts=[5, 6])
|
|
122
118
|
```
|
|
123
119
|
|
|
120
|
+
# String / char[] Type
|
|
121
|
+
|
|
122
|
+
Defining c-string types is a little different. Instead of using
|
|
123
|
+
`size` in the `TypeMeta`, we need to instead use `chunk_size`.
|
|
124
|
+
|
|
125
|
+
This is because the way the struct format is defined for c-strings needs
|
|
126
|
+
to know how big the string data is expected to be so that it can put the
|
|
127
|
+
whole string in a single variable.
|
|
128
|
+
|
|
129
|
+
The `chunk_size` is also introduced to allow for `char[][]` for converting
|
|
130
|
+
a list of strings.
|
|
131
|
+
|
|
132
|
+
```c
|
|
133
|
+
struct MyStruct {
|
|
134
|
+
char myStr[3];
|
|
135
|
+
char myStrList[2][3];
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
```python
|
|
139
|
+
class MyStruct(StructDataclass):
|
|
140
|
+
myStr: Annotated[string_t, TypeMeta[str](chunk_size=3)]
|
|
141
|
+
myStrList: Annotated[list[string_t], TypeMeta[str](size=2, chunk_size=3)]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
s = MyStruct()
|
|
145
|
+
s.decode([65, 66, 67, 68, 69, 70, 71, 72, 73])
|
|
146
|
+
# MyStruct(myStr=b"ABC", myStrList=[b"DEF", b"GHI"])
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If you instead try to define this as a list of `char_t` types,
|
|
150
|
+
you would only be able to end up with
|
|
151
|
+
`MyStruct(myStr=[b"A", b"B", b"C"], myStrList=[b"D", b"E", b"F", b"G", b"H", b"I"])`
|
|
152
|
+
|
|
124
153
|
# The Bits Abstraction
|
|
125
154
|
|
|
126
155
|
This library includes a `bits` abstraction to map bits to variables for easier access.
|
|
@@ -136,8 +165,9 @@ enum ConfigFlags {
|
|
|
136
165
|
```
|
|
137
166
|
|
|
138
167
|
```python
|
|
139
|
-
|
|
140
|
-
|
|
168
|
+
class FlagsType(BitsType):
|
|
169
|
+
__bits_type__ = uint8_t
|
|
170
|
+
__bits_definition__ = {"lights_flag": 0, "platform_flag": 1}
|
|
141
171
|
|
|
142
172
|
f = FlagsType()
|
|
143
173
|
f.decode([3])
|
|
@@ -167,7 +197,6 @@ struct MyStruct {
|
|
|
167
197
|
```
|
|
168
198
|
|
|
169
199
|
```python
|
|
170
|
-
@struct_dataclass
|
|
171
200
|
class EnabledSensors(StructDataclass):
|
|
172
201
|
# We can define the actual data we are ingesting here
|
|
173
202
|
# This mirrors the `uint8_t enabledSensors[5]` data
|
|
@@ -228,7 +257,6 @@ s.decode([15, 15, 15, 15, 0])
|
|
|
228
257
|
# [False, False, False, False],
|
|
229
258
|
# [True, True, True, True],
|
|
230
259
|
# [False, False, False, False],
|
|
231
|
-
# [False, False, False, False]
|
|
232
260
|
# ]
|
|
233
261
|
|
|
234
262
|
# With the get/set functioned defined, we can access the data
|
|
@@ -254,15 +282,13 @@ struct LEDS {
|
|
|
254
282
|
```
|
|
255
283
|
|
|
256
284
|
```python
|
|
257
|
-
@struct_dataclass
|
|
258
285
|
class RGB(StructDataclass):
|
|
259
286
|
r: uint8_t
|
|
260
287
|
g: uint8_t
|
|
261
288
|
b: uint8_t
|
|
262
289
|
|
|
263
|
-
@struct_dataclass
|
|
264
290
|
class LEDS(StructDataclass):
|
|
265
|
-
lights: Annotated[list[RGB], TypeMeta(size=3
|
|
291
|
+
lights: Annotated[list[RGB], TypeMeta(size=3)]
|
|
266
292
|
|
|
267
293
|
l = LEDS()
|
|
268
294
|
l.decode([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
@@ -272,11 +298,10 @@ l.decode([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
|
272
298
|
# Future Updates
|
|
273
299
|
|
|
274
300
|
- Bitfield: Similar to the `Bits` abstraction. An easy way to define bitfields
|
|
275
|
-
- C-Strings: Make a base class to handle C strings (arrays of chars)
|
|
276
301
|
- Potentially more ways to define bits (dicts/lists/etc).
|
|
277
302
|
- Potentially allowing list defaults to be entire pre-defined lists.
|
|
278
303
|
- ???
|
|
279
304
|
|
|
280
305
|
# Examples
|
|
281
306
|
|
|
282
|
-
You can see a more fully fledged example in the `test/examples.py` file.
|
|
307
|
+
You can see a more fully fledged example in the `test/examples.py` file.
|
|
@@ -34,7 +34,6 @@ struct MyStruct {
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
```python
|
|
37
|
-
@struct_dataclass
|
|
38
37
|
class MyStruct(StructDataclass):
|
|
39
38
|
myNum: int16_t
|
|
40
39
|
myLetter: char_t
|
|
@@ -61,7 +60,6 @@ struct MyStruct {
|
|
|
61
60
|
};
|
|
62
61
|
```
|
|
63
62
|
```python
|
|
64
|
-
@struct_dataclass
|
|
65
63
|
class MyStruct(StructDataclass):
|
|
66
64
|
myInts: Annotated[list[uint8_t], TypeMeta(size=4)]
|
|
67
65
|
myBiggerInts: Annotated[list[uint16_t], TypeMeta(size=2)]
|
|
@@ -87,7 +85,6 @@ struct MyStruct {
|
|
|
87
85
|
```
|
|
88
86
|
|
|
89
87
|
```python
|
|
90
|
-
@struct_dataclass
|
|
91
88
|
class MyStruct(StructDataclass):
|
|
92
89
|
myInt: uint8_t = 5
|
|
93
90
|
myInts: Annnotated[list[uint8_t], TypeMeta(size=2, default=1)]
|
|
@@ -98,6 +95,39 @@ s.decode([10, 5, 6])
|
|
|
98
95
|
# MyStruct(myInt=10, myInts=[5, 6])
|
|
99
96
|
```
|
|
100
97
|
|
|
98
|
+
# String / char[] Type
|
|
99
|
+
|
|
100
|
+
Defining c-string types is a little different. Instead of using
|
|
101
|
+
`size` in the `TypeMeta`, we need to instead use `chunk_size`.
|
|
102
|
+
|
|
103
|
+
This is because the way the struct format is defined for c-strings needs
|
|
104
|
+
to know how big the string data is expected to be so that it can put the
|
|
105
|
+
whole string in a single variable.
|
|
106
|
+
|
|
107
|
+
The `chunk_size` is also introduced to allow for `char[][]` for converting
|
|
108
|
+
a list of strings.
|
|
109
|
+
|
|
110
|
+
```c
|
|
111
|
+
struct MyStruct {
|
|
112
|
+
char myStr[3];
|
|
113
|
+
char myStrList[2][3];
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
```python
|
|
117
|
+
class MyStruct(StructDataclass):
|
|
118
|
+
myStr: Annotated[string_t, TypeMeta[str](chunk_size=3)]
|
|
119
|
+
myStrList: Annotated[list[string_t], TypeMeta[str](size=2, chunk_size=3)]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
s = MyStruct()
|
|
123
|
+
s.decode([65, 66, 67, 68, 69, 70, 71, 72, 73])
|
|
124
|
+
# MyStruct(myStr=b"ABC", myStrList=[b"DEF", b"GHI"])
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If you instead try to define this as a list of `char_t` types,
|
|
128
|
+
you would only be able to end up with
|
|
129
|
+
`MyStruct(myStr=[b"A", b"B", b"C"], myStrList=[b"D", b"E", b"F", b"G", b"H", b"I"])`
|
|
130
|
+
|
|
101
131
|
# The Bits Abstraction
|
|
102
132
|
|
|
103
133
|
This library includes a `bits` abstraction to map bits to variables for easier access.
|
|
@@ -113,8 +143,9 @@ enum ConfigFlags {
|
|
|
113
143
|
```
|
|
114
144
|
|
|
115
145
|
```python
|
|
116
|
-
|
|
117
|
-
|
|
146
|
+
class FlagsType(BitsType):
|
|
147
|
+
__bits_type__ = uint8_t
|
|
148
|
+
__bits_definition__ = {"lights_flag": 0, "platform_flag": 1}
|
|
118
149
|
|
|
119
150
|
f = FlagsType()
|
|
120
151
|
f.decode([3])
|
|
@@ -144,7 +175,6 @@ struct MyStruct {
|
|
|
144
175
|
```
|
|
145
176
|
|
|
146
177
|
```python
|
|
147
|
-
@struct_dataclass
|
|
148
178
|
class EnabledSensors(StructDataclass):
|
|
149
179
|
# We can define the actual data we are ingesting here
|
|
150
180
|
# This mirrors the `uint8_t enabledSensors[5]` data
|
|
@@ -205,7 +235,6 @@ s.decode([15, 15, 15, 15, 0])
|
|
|
205
235
|
# [False, False, False, False],
|
|
206
236
|
# [True, True, True, True],
|
|
207
237
|
# [False, False, False, False],
|
|
208
|
-
# [False, False, False, False]
|
|
209
238
|
# ]
|
|
210
239
|
|
|
211
240
|
# With the get/set functioned defined, we can access the data
|
|
@@ -231,15 +260,13 @@ struct LEDS {
|
|
|
231
260
|
```
|
|
232
261
|
|
|
233
262
|
```python
|
|
234
|
-
@struct_dataclass
|
|
235
263
|
class RGB(StructDataclass):
|
|
236
264
|
r: uint8_t
|
|
237
265
|
g: uint8_t
|
|
238
266
|
b: uint8_t
|
|
239
267
|
|
|
240
|
-
@struct_dataclass
|
|
241
268
|
class LEDS(StructDataclass):
|
|
242
|
-
lights: Annotated[list[RGB], TypeMeta(size=3
|
|
269
|
+
lights: Annotated[list[RGB], TypeMeta(size=3)]
|
|
243
270
|
|
|
244
271
|
l = LEDS()
|
|
245
272
|
l.decode([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
@@ -249,11 +276,10 @@ l.decode([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
|
249
276
|
# Future Updates
|
|
250
277
|
|
|
251
278
|
- Bitfield: Similar to the `Bits` abstraction. An easy way to define bitfields
|
|
252
|
-
- C-Strings: Make a base class to handle C strings (arrays of chars)
|
|
253
279
|
- Potentially more ways to define bits (dicts/lists/etc).
|
|
254
280
|
- Potentially allowing list defaults to be entire pre-defined lists.
|
|
255
281
|
- ???
|
|
256
282
|
|
|
257
283
|
# Examples
|
|
258
284
|
|
|
259
|
-
You can see a more fully fledged example in the `test/examples.py` file.
|
|
285
|
+
You can see a more fully fledged example in the `test/examples.py` file.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import tomllib
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
|
|
4
|
+
from sphinx.application import Sphinx
|
|
5
|
+
|
|
4
6
|
# Sphinx Base --------------------------------------------------------------------------
|
|
5
7
|
# Extensions
|
|
6
8
|
extensions = [
|
|
@@ -61,7 +63,7 @@ exclude_patterns = ["autoapi_templates"]
|
|
|
61
63
|
|
|
62
64
|
|
|
63
65
|
# Add any Sphinx plugin settings here that don't have global variables exposed.
|
|
64
|
-
def setup(app):
|
|
66
|
+
def setup(app: Sphinx) -> None:
|
|
65
67
|
# App Settings ---------------------------------------------------------------------
|
|
66
68
|
# Set source filetype(s)
|
|
67
69
|
# Allow .rst files
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pystructtype"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Leverage Python Types to Define C-Struct Interfaces"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{name = "fchorney", email = "github@djsbx.com"}]
|
|
@@ -13,13 +13,11 @@ classifiers = [
|
|
|
13
13
|
"Operating System :: OS Independent",
|
|
14
14
|
"Intended Audience :: Developers",
|
|
15
15
|
"Topic :: Utilities",
|
|
16
|
-
"Programming Language :: Python :: 3.
|
|
16
|
+
"Programming Language :: Python :: 3.14",
|
|
17
17
|
]
|
|
18
18
|
keywords = ["struct", "cstruct", "type"]
|
|
19
|
-
requires-python = ">=3.
|
|
20
|
-
dependencies = [
|
|
21
|
-
"loguru>=0.7.3",
|
|
22
|
-
]
|
|
19
|
+
requires-python = ">=3.14,<4.0"
|
|
20
|
+
dependencies = []
|
|
23
21
|
|
|
24
22
|
[project.urls]
|
|
25
23
|
Homepage = "https://github.com/fchorney/pystructtype"
|
|
@@ -29,22 +27,28 @@ Issues = "https://github.com/fchorney/pystructtype/issues"
|
|
|
29
27
|
|
|
30
28
|
[dependency-groups]
|
|
31
29
|
dev = [
|
|
32
|
-
"coverage[toml]",
|
|
33
|
-
"mypy",
|
|
34
|
-
"
|
|
35
|
-
"pytest
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"sphinx
|
|
39
|
-
"sphinx-
|
|
40
|
-
"
|
|
41
|
-
"tox
|
|
30
|
+
"coverage[toml]>=7.6.12",
|
|
31
|
+
"mypy>=1.19.1",
|
|
32
|
+
"pre-commit>=4.5.1",
|
|
33
|
+
"pytest>=9.0.2",
|
|
34
|
+
"pytest-cov>=6.3.0",
|
|
35
|
+
"ruff>=0.15.6",
|
|
36
|
+
"sphinx>=9.1.0",
|
|
37
|
+
"sphinx-autoapi>=3.8.0",
|
|
38
|
+
"sphinx-rtd-theme>=3.1.0",
|
|
39
|
+
"tox>=4.49.1",
|
|
40
|
+
"tox-uv>=1.33.4",
|
|
42
41
|
]
|
|
43
42
|
|
|
44
43
|
[build-system]
|
|
45
44
|
requires = ["hatchling"]
|
|
46
45
|
build-backend = "hatchling.build"
|
|
47
46
|
|
|
47
|
+
[tool.mypy]
|
|
48
|
+
strict = false
|
|
49
|
+
check_untyped_defs = true
|
|
50
|
+
exclude = ["venv", ".venv"]
|
|
51
|
+
|
|
48
52
|
[tool.coverage.run]
|
|
49
53
|
relative_files = true
|
|
50
54
|
|
|
@@ -58,30 +62,39 @@ exclude_lines = ["if __name__ == .__main__.:", "def __str__", "def __repr__", "p
|
|
|
58
62
|
package = true
|
|
59
63
|
|
|
60
64
|
[tool.ruff]
|
|
65
|
+
target-version = "py314"
|
|
61
66
|
line-length = 120
|
|
62
67
|
indent-width = 4
|
|
63
|
-
|
|
64
|
-
target-version = "py313"
|
|
68
|
+
exclude = []
|
|
65
69
|
|
|
66
70
|
[tool.ruff.lint]
|
|
67
71
|
select = [
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
72
|
+
"E", # pycodestyle errors
|
|
73
|
+
"W", # pycodestyle warnings
|
|
74
|
+
"F", # pyflakes
|
|
75
|
+
"I", # isort
|
|
76
|
+
"N", # pep8-naming
|
|
77
|
+
"B", # flake8-bugbear
|
|
78
|
+
"C4", # flake8-comprehensions
|
|
79
|
+
"UP", # pyupgrade
|
|
80
|
+
"ARG001", # unused arguments in functions
|
|
81
|
+
"RUF", # ruff specific rules
|
|
82
|
+
]
|
|
83
|
+
ignore = [
|
|
84
|
+
"B008", # do not perform function calls in argument defaults
|
|
85
|
+
"W191", # indentation contains tabs
|
|
75
86
|
]
|
|
76
|
-
ignore = []
|
|
77
87
|
fixable = ["ALL"]
|
|
78
88
|
unfixable = []
|
|
79
89
|
|
|
80
90
|
[tool.ruff.format]
|
|
81
|
-
# Like Black
|
|
82
91
|
quote-style = "double"
|
|
83
92
|
indent-style = "space"
|
|
84
93
|
skip-magic-trailing-comma = false
|
|
85
94
|
line-ending = "auto"
|
|
86
95
|
docstring-code-format = true
|
|
87
96
|
docstring-code-line-length = "dynamic"
|
|
97
|
+
|
|
98
|
+
[tool.ruff.lint.pyupgrade]
|
|
99
|
+
# Preserve types, even if a file imports `from __future__ import annotations`.
|
|
100
|
+
keep-runtime-typing = true
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from pystructtype.bitstype import BitsType
|
|
2
|
-
from pystructtype.structdataclass import StructDataclass
|
|
1
|
+
from pystructtype.bitstype import BitsType
|
|
2
|
+
from pystructtype.structdataclass import StructDataclass
|
|
3
3
|
from pystructtype.structtypes import (
|
|
4
4
|
TypeInfo,
|
|
5
5
|
TypeMeta,
|
|
@@ -10,6 +10,7 @@ from pystructtype.structtypes import (
|
|
|
10
10
|
int16_t,
|
|
11
11
|
int32_t,
|
|
12
12
|
int64_t,
|
|
13
|
+
string_t,
|
|
13
14
|
uint8_t,
|
|
14
15
|
uint16_t,
|
|
15
16
|
uint32_t,
|
|
@@ -21,7 +22,6 @@ __all__ = [
|
|
|
21
22
|
"StructDataclass",
|
|
22
23
|
"TypeInfo",
|
|
23
24
|
"TypeMeta",
|
|
24
|
-
"bits",
|
|
25
25
|
"char_t",
|
|
26
26
|
"double_t",
|
|
27
27
|
"float_t",
|
|
@@ -29,7 +29,7 @@ __all__ = [
|
|
|
29
29
|
"int16_t",
|
|
30
30
|
"int32_t",
|
|
31
31
|
"int64_t",
|
|
32
|
-
"
|
|
32
|
+
"string_t",
|
|
33
33
|
"uint8_t",
|
|
34
34
|
"uint16_t",
|
|
35
35
|
"uint32_t",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from dataclasses import field
|
|
4
|
+
from types import MappingProxyType
|
|
5
|
+
from typing import Annotated, ClassVar
|
|
6
|
+
|
|
7
|
+
from pystructtype.structdataclass import StructDataclass
|
|
8
|
+
from pystructtype.structtypes import TypeMeta
|
|
9
|
+
from pystructtype.utils import int_to_bool_list
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BitsType(StructDataclass):
|
|
13
|
+
"""
|
|
14
|
+
Base class for bitfield structs. Subclasses must define __bits_type__ and __bits_definition__.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__bits_type__: ClassVar[type]
|
|
18
|
+
__bits_definition__: ClassVar[dict[str, int | list[int]] | Mapping[str, int | list[int]]]
|
|
19
|
+
|
|
20
|
+
_raw: int
|
|
21
|
+
_meta: dict[str, int | list[int]]
|
|
22
|
+
|
|
23
|
+
def __init_subclass__(cls, **kwargs):
|
|
24
|
+
super().__init_subclass__(**kwargs)
|
|
25
|
+
# Check for required attributes
|
|
26
|
+
if not hasattr(cls, "__bits_type__") or not hasattr(cls, "__bits_definition__"):
|
|
27
|
+
raise TypeError(
|
|
28
|
+
"Subclasses of BitsType must define __bits_type__ and __bits_definition__ class attributes."
|
|
29
|
+
)
|
|
30
|
+
bits_type = cls.__bits_type__
|
|
31
|
+
definition = cls.__bits_definition__
|
|
32
|
+
|
|
33
|
+
# Automatically wrap in MappingProxyType if it's a dict and not already immutable
|
|
34
|
+
if isinstance(definition, dict) and not isinstance(definition, MappingProxyType):
|
|
35
|
+
definition = MappingProxyType(definition)
|
|
36
|
+
cls.__bits_definition__ = definition
|
|
37
|
+
|
|
38
|
+
# # Remove __bits_type__ and __bits_definition__ from __annotations__ if present
|
|
39
|
+
# cls.__annotations__.pop("__bits_type__", None)
|
|
40
|
+
# cls.__annotations__.pop("__bits_definition__", None)
|
|
41
|
+
|
|
42
|
+
# Set the correct type for the raw data
|
|
43
|
+
cls._raw = 0
|
|
44
|
+
cls.__annotations__["_raw"] = bits_type
|
|
45
|
+
|
|
46
|
+
cls._meta = field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
# Create the defined attributes, defaults, and annotations in the class
|
|
49
|
+
for key, value in definition.items():
|
|
50
|
+
if isinstance(value, list):
|
|
51
|
+
setattr(
|
|
52
|
+
cls,
|
|
53
|
+
key,
|
|
54
|
+
field(default_factory=lambda v=len(value): [False for _ in range(v)]), # type: ignore
|
|
55
|
+
)
|
|
56
|
+
cls.__annotations__[key] = Annotated[list[bool], TypeMeta(size=len(value))]
|
|
57
|
+
else:
|
|
58
|
+
setattr(cls, key, False)
|
|
59
|
+
cls.__annotations__[key] = bool
|
|
60
|
+
|
|
61
|
+
def __post_init__(self) -> None:
|
|
62
|
+
super().__post_init__()
|
|
63
|
+
self._meta = dict(self.__bits_definition__)
|
|
64
|
+
|
|
65
|
+
def _decode(self, data: list[int]) -> None:
|
|
66
|
+
super()._decode(data)
|
|
67
|
+
bin_data = int_to_bool_list(self._raw, self._byte_length)
|
|
68
|
+
for k, v in self._meta.items():
|
|
69
|
+
if isinstance(v, list):
|
|
70
|
+
steps = [bin_data[idx] for idx in v]
|
|
71
|
+
setattr(self, k, steps)
|
|
72
|
+
else:
|
|
73
|
+
setattr(self, k, bin_data[v])
|
|
74
|
+
|
|
75
|
+
def _encode(self) -> list[int]:
|
|
76
|
+
bin_data = list(itertools.repeat(False, self._byte_length * 8))
|
|
77
|
+
for k, v in self._meta.items():
|
|
78
|
+
if isinstance(v, list):
|
|
79
|
+
steps = getattr(self, k)
|
|
80
|
+
for idx, bit_idx in enumerate(v):
|
|
81
|
+
bin_data[bit_idx] = steps[idx]
|
|
82
|
+
else:
|
|
83
|
+
bin_data[v] = getattr(self, k)
|
|
84
|
+
self._raw = sum(int(v) << i for i, v in enumerate(bin_data))
|
|
85
|
+
# Return _raw as a list of bytes (little-endian)
|
|
86
|
+
return super()._encode()
|