Skip to content

Commit 0477c67

Browse files
authored
v2.8.0: Extended syntax & typing fixes
Merge pull request #55 from MatrixEditor/dev/2.8.0-extended-syntax
2 parents e2d720f + e08fd0f commit 0477c67

135 files changed

Lines changed: 7684 additions & 4613 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
# Changelog
22

3+
## [2.8.0] - Extended Syntax
4+
5+
### Added
6+
7+
- New concept: *extended* syntax featuring typing compliance; introduces shortcut `f` to annotate fields and `Invisible()` to hide fields from the constructor
8+
- `struct_factory.mixin` and `bitfield_factory.mixin` both provide default function wrappers for packing and unpacking data as well as typing fixes for operating on types directly.
9+
- New `parentctx` context path
10+
- Dynamic endian is now used by default (i. e. can be changed via order parameter in pack() or unpack())
11+
- New generic `Timestamp` class
12+
- A number of new test cases
13+
- New module: `caterpillar.types`: defines default types that can be used as annotations within struct definitions
14+
- `O_DEFAULT_ENDIAN` as a global option to set a global default byteorder
15+
- `O_DEFAULT_ARCH` same concept for arch-like objects
16+
- new 'strict' option to `Enum` struct
17+
- `ATTR_PACK` and `ATTR_UNPACK` to *caterpillar.shared*
18+
- `'order'` and `'arch'` options to pack() and unpack(), which temporarily change the global endianess or arch (compatible with Dynamic byteorder)
19+
20+
### Changes
21+
22+
- Merge all stub files with their corresponding Python files. All typing is now inline.
23+
- `padding` struct now has its own dedicated class
24+
- sizeof() now always returns an integer
25+
- ContextPath: dropped support for the call action
26+
- Field.get_name() now always returns a string
27+
- Rename `ssize` and `size` to `pssize` and `psize` in caterpillar.fields exports
28+
- options.get_flags() always returns a list
29+
30+
### Fixes
31+
32+
- IntFlag support for `Enum` structs
33+
- Fix incorrect Sha1 Digest length
34+
- Add missing global exports in `caterpillar.py`
35+
- Bitfield, Struct and Sequence now respect fields with already configured byteorder
36+
- Prefixed struct now does not require `as_field=True` when calling pack() or unpack()
37+
338
## [2.7.0] - Dynamic Byteorder
439

540
### Added

README.md

Lines changed: 78 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,83 @@ options will be added in the future. Documentation is [here >](https://matrixedi
2323
* insert proper types into the class definition to support documentation and
2424
* it helps you to create cleaner and more compact code.
2525
* There is also a feature that lets you dynamically change the endian within a struct!
26-
* You can even extend Caterpillar and write your parsing logic in C or C++!!
26+
* You can even extend Caterpillar and write your parsing logic in C or C++
27+
* All struct definitions can be typing compliant!!! (tested with pyright)
2728

28-
> [!NOTE]
29-
> Python 3.14 breaks `with` statements in class definitions since `__annotations__` are added at the end
30-
> of a class definition. Therefore, `Digest` and conditional statements **ARE NOT SUPPORTED** using the `with` syntax in Python 3.14+.
31-
> As of version `2.4.5` the `Digest` class has a counterpart (`DigestField`), which can be used to manually specify a digest without
32-
> the need of a `ẁith` statement.
29+
## Give me some code!
3330

31+
*The following code is typing compliant, meaning your static type checker won't*
32+
*scream at you when developing with this code*.
3433

35-
## Give me some code!
34+
<details>
35+
<summary><i>If you want to check out the default syntax, open this block.</i></summary>
3636

3737
```python
3838
from caterpillar.py import *
39+
from caterpillar.types import *
3940

4041
@bitfield(order=LittleEndian)
4142
class Header:
42-
version : 4 # 4bit integer
43-
valid : 1 # 1bit flag (boolean)
44-
ident : (8, CharFactory) # 8bit char
43+
version : 4 # 4bit integer
44+
valid : 1 # 1bit flag (boolean)
45+
ident : (8, CharFactory) # 8bit char
4546
# automatic alignment to 16bits
4647

47-
@struct(order=LittleEndian)
48+
THE_KEY = b"ITS MAGIC"
49+
50+
@struct(order=LittleEndian, kw_only=True)
4851
class Format:
49-
magic : b"ITS MAGIC" # Supports string and byte constants directly
52+
magic : THE_KEY # Supports string and byte constants directly
5053
header : Header
51-
a : uint8 # Primitive data types
52-
b : Dynamic + int32 # dynamic endian based on global config
53-
length : uint8 # String fields with computed lengths
54-
name : String(this.length) # -> you can also use Prefixed(uint8)
54+
a : uint8 # Primitive data types
55+
b : Dynamic + int32 # dynamic endian based on global config
56+
length : uint8 # String fields with computed lengths
57+
name : String(this.length) # -> you can also use Prefixed(uint8)
58+
59+
# custom actions, e.g. for hashes
60+
_hash_begin : DigestField.begin("hash", Md5_Algo)
61+
# Sequences with prefixed, computed lengths -+ part of the MD5 hash
62+
names : CString[uint8::] # |
63+
# -+
64+
# automatic hash creation and verification + default value
65+
hash : Md5_Field("hash", verify=True)
66+
67+
# Creation, packing and unpacking remains the same
68+
```
5569

56-
_hash_begin : DigestField.begin("hash", Md5_Algo) # custom actions, e.g. for hashes
57-
names : CString[uint8::] # Sequences with prefixed, computed lengths
58-
hash : Md5_Field("hash", verify=True) = None # automatic hash creation and verification + default value
70+
</details>
5971

60-
# Instantiation (keyword-only arguments, magic is auto-inferred):
72+
```python
73+
from caterpillar.py import *
74+
from caterpillar.types import *
75+
76+
@bitfield(order=LittleEndian)
77+
class Header:
78+
version : int4_t # 4bit integer
79+
valid : int1_t # 1bit flag (boolean)
80+
ident : f[str, (8, CharFactory)] # 8bit char
81+
# automatic alignment to 16bits
82+
83+
THE_KEY = b"ITS MAGIC"
84+
85+
@struct(order=LittleEndian, kw_only=True)
86+
class Format:
87+
magic : f[bytes, THE_KEY] = THE_KEY # Supports string and byte constants directly
88+
header : Header
89+
a : uint8_t # Primitive data types
90+
b : f[int, Dynamic + int32] # dynamic endian based on global config
91+
length : uint8_t # String fields with computed lengths
92+
name : f[str, String(this.length)] # -> you can also use Prefixed(uint8)
93+
94+
# custom actions, e.g. for hashes
95+
_hash_begin : f[None, DigestField.begin("hash", Md5_Algo)] = None
96+
# Sequences with prefixed, computed lengths -+ part of the MD5 hash
97+
names : f[list[str], CString[uint8::]] # |
98+
# -+
99+
# automatic hash creation and verification + default value
100+
hash : f[bytes, Md5_Field("hash", verify=True)] = b""
101+
102+
# Creation (keyword-only arguments, magic is auto-inferred):
61103
obj = Format(
62104
header=Header(version=2, valid=True, ident="F"),
63105
a=1,
@@ -66,22 +108,32 @@ obj = Format(
66108
name="foo",
67109
names=["a", "b"]
68110
)
69-
# Packing the object, reads as 'PACK obj FROM Format'
111+
112+
# Packing the object; reads as 'PACK obj FROM Format'
70113
# objects of struct classes can be packed right away
71-
blob = pack(obj, Format)
114+
data_le = pack(obj, Format)
72115
# results in: b'ITS MAGIC0*\x01\x02\x00\x00\x00\x03foo\x02a\x00b\x00)\x9a...'
73116

74117
# Unpacking the binary data, reads as 'UNPACK Format FROM blob'
75-
obj2 = unpack(Format, blob)
118+
obj2 = unpack(Format, data_le)
119+
assert obj2.names == obj.names
76120

77-
# to pack with a different endian for field 'b', use _order
78-
data = pack(obj, Format, _order=BigEndian)
121+
# to pack with a different endian for fields 'a' and 'b', use 'order'
122+
data_be = pack(obj, Format, order=BigEndian)
123+
assert data_le != data_be
79124
```
80125

81-
This library offers extensive functionality beyond basic struct handling. For further details
126+
> [!NOTE]
127+
> Python 3.14 breaks `with` statements in class definitions since `__annotations__` are added at the end
128+
> of a class definition. Therefore, `Digest` and conditional statements **ARE NOT SUPPORTED** using the `with` syntax in Python 3.14+.
129+
> As of version `2.4.5` the `Digest` class has a counterpart (`DigestField`), which can be used to manually specify a digest without
130+
> the need of a `ẁith` statement.
131+
132+
This library offers extensive functionality beyond basic struct definitions. For further details
82133
on its powerful features, explore the official [documentation](https://matrixeditor.github.io/caterpillar/),
83134
[examples](./examples/), and [test cases](./test/).
84135

136+
85137
## Installation
86138

87139
> [!NOTE]

docs/sphinx/source/development/changelog.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,48 @@ Changelog
66

77
*More entries will be added in the future.*
88

9+
.. _changelog_2.8.0:
10+
11+
[2.8.0] - Extended Syntax
12+
=========================
13+
14+
Added
15+
-----
16+
17+
- New concept: *extended* syntax featuring typing compliance; introduces shortcut :data:`f` to annotate fields and :func:`~caterpillar.model.Invisible` to hide fields from the constructor
18+
- ``struct_factory.mixin`` and ``bitfield_factory.mixin`` both provide default function wrappers for packing and unpacking data as well as typing fixes for operating on types directly.
19+
- New :data:`~caterpillar.context.parentctx` context path
20+
- Dynamic endian is now used by default (i. e. can be changed via order parameter in pack() or unpack())
21+
- New generic :class:`~caterpillar.fields.Timestamp` class
22+
- A number of new test cases
23+
- New module: ``caterpillar.types``: defines default types that can be used as annotations within struct definitions
24+
- :data:`~caterpillar.byteorder.O_DEFAULT_ENDIAN` as a global option to set a global default byteorder
25+
- :data:`~caterpillar.byteorder.O_DEFAULT_ARCH` same concept for arch-like objects
26+
- new 'strict' option to :class:`~caterpillar.fields.Enum` struct
27+
- :data:`~caterpillar.shared.ATTR_PACK` and :data:`~caterpillar.shared.ATTR_UNPACK` to *caterpillar.shared*
28+
- `'order'` and `'arch'` options to pack() and unpack(), which temporarily change the global endianess or arch (compatible with Dynamic byteorder)
29+
30+
Changes
31+
-------
32+
33+
- Merge all stub files with their corresponding Python files. All typing is now inline.
34+
- :class:`~caterpillar.fields.Padding` struct now has its own dedicated class
35+
- sizeof() now always returns an integer
36+
- ContextPath: dropped support for the call action
37+
- Field.get_name() now always returns a string
38+
- Rename `ssize` and `size` to :data:`~caterpillar.fields.pssize` and :data:`~caterpillar.fields.psize` in caterpillar.fields exports
39+
- options.get_flags() always returns a list
40+
41+
Fixes
42+
-----
43+
44+
- IntFlag support for :class:`~caterpillar.fields.Enum` structs
45+
- Fix incorrect Sha1 Digest length
46+
- Add missing global exports in *caterpillar.py*
47+
- Bitfield, Struct and Sequence now respect fields with already configured byteorder
48+
- Prefixed struct now does not require ``as_field=True`` when calling pack() or unpack()
49+
50+
951
.. _changelog_2.7.0:
1052

1153
[2.7.0] - Dynamic Byteorder

docs/sphinx/source/index.rst

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,33 @@ efficient.
1616
structs that adjust their size based on the current context. This framework enables you
1717
to write complex structures in a compact and readable manner.
1818

19+
.. tab-set::
1920

20-
.. code-block::
21-
:caption: Simple example of a custom struct
21+
.. tab-item:: Default Syntax
22+
23+
.. code-block::
24+
:caption: Simple example of a custom struct
25+
26+
@struct
27+
class Format:
28+
magic : b"Foo" # constant values
29+
name : CString(...) # \x00-terminated String without a fixed length
30+
value : le + uint16 # little endian encoding
31+
entries: be + CString[uint32::] # arrays with big-endian prefixed length
32+
33+
34+
.. tab-item:: Extended Syntax (>=2.8.0)
35+
36+
.. code-block::
37+
:caption: Simple example of a custom struct with type annotations in-place
38+
39+
@struct(kw_only=True) # keyword-only constructor arguments
40+
class Format:
41+
magic : f[bytes, b"Foo"] = Invisible() # constant values
42+
name : cstr_t # \x00-terminated String without a fixed length
43+
value : f[int, le + uint16] # little endian encoding
44+
entries: f[list[str], be + CString[uint32::]] # arrays with big-endian prefixed length
2245
23-
@struct
24-
class Format:
25-
magic: b"Foo" # constant values
26-
name: CString(...) # \x00-terminated String without a fixed length
27-
value: le + uint16 # little endian encoding
28-
entries: be + CString[uint32::] # arrays with big-endian prefixed length
2946
3047
3148
.. admonition:: Hold up, wait a minute!
@@ -58,6 +75,9 @@ Format(magic=b'Foo', name='Hello, World!', value=10, entries=['Bar', 'Baz'])
5875
to 4 times. More information are provided when discussing available configuration
5976
:ref:`options`.
6077

78+
- And *YES*, this library tries to enforce strong typing wherever appliacable and supported.
79+
More on that topic later in the tutorial: :ref:`first_steps-extended-syntax`
80+
6181

6282
Where to start?
6383
---------------

docs/sphinx/source/library/byteorder.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,27 @@ Byteorder
1515
.. autoclass:: DynByteOrder
1616
:members:
1717

18-
.. autofunction:: byteorder(obj, default: Optional[ByteOrder] = None) -> ByteOrder
18+
.. autofunction:: byteorder
1919
.. autofunction:: byteorder_is_little
2020

2121
Standard Byteorder Instances
2222
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2323

24-
.. autoattribute:: caterpillar.byteorder.Native
24+
.. autodata:: caterpillar.byteorder.Native
2525

26-
.. autoattribute:: caterpillar.byteorder.BigEndian
26+
.. autodata:: caterpillar.byteorder.BigEndian
2727

28-
.. autoattribute:: caterpillar.byteorder.LittleEndian
28+
.. autodata:: caterpillar.byteorder.LittleEndian
2929

30-
.. autoattribute:: caterpillar.byteorder.NetEndian
30+
.. autodata:: caterpillar.byteorder.NetEndian
3131

32-
.. autoattribute:: caterpillar.byteorder.SysNative
32+
.. autodata:: caterpillar.byteorder.SysNative
3333

34-
.. autoattribute:: caterpillar.byteorder.Dynamic
34+
.. autodata:: caterpillar.byteorder.Dynamic
3535

36+
.. autodata:: caterpillar.byteorder.LITTLE_ENDIAN_FMT
37+
38+
.. autodata:: caterpillar.byteorder.O_DEFAULT_ENDIAN
3639

3740

3841
Architecture

docs/sphinx/source/library/context.rst

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Context classes
1717

1818

1919
.. autoclass:: caterpillar.context.ContextPath
20-
:members:
20+
:members: __call__, __getattribute__, __repr__, parent, parentctx
2121

2222
.. autoclass:: caterpillar.context.ContextLength
2323
:members:
@@ -29,6 +29,10 @@ Context classes
2929
.. autoclass:: caterpillar.context.ConditionContext
3030

3131

32+
.. autoclass:: caterpillar.context.SetContextVar
33+
:members: __action_pack__, __action_unpack__
34+
35+
3236
Extra options
3337
-------------
3438

@@ -52,17 +56,15 @@ Extra options
5256
Special paths
5357
-------------
5458

55-
.. autoattribute:: caterpillar.context.this
59+
.. autodata:: caterpillar.context.this
5660

57-
.. autoattribute:: caterpillar.context.ctx
61+
.. autodata:: caterpillar.context.ctx
5862

59-
.. autoattribute:: caterpillar.context.parent
63+
.. autodata:: caterpillar.context.parent
6064

61-
.. autoattribute:: caterpillar.context.root
65+
.. autodata:: caterpillar.context.parentctx
6266

63-
Provides access to the root-level context object.
64-
65-
.. versionadded:: 2.6.0
67+
.. autodata:: caterpillar.context.root
6668

6769

6870
Special Attributes
@@ -112,6 +114,18 @@ Special Attributes
112114

113115
Points to the root of the entire context hierarchy.
114116

117+
.. autoattribute:: caterpillar.context.CTX_ORDER
118+
119+
Stores the currently used endianess (only in root context).
120+
121+
.. versionadded:: 2.7.0
122+
123+
.. autoattribute:: caterpillar.context.CTX_ARCH
124+
125+
Stores the currently used architecture (only in root context).
126+
127+
.. versionadded:: 2.7.0
128+
115129

116130
Expressions
117131
-----------
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _library-extended-types:
2+
3+
Types (Extended Syntax)
4+
=======================
5+
6+
.. automodule:: caterpillar.types
7+
:members:

0 commit comments

Comments
 (0)