Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Run Tests

on: [pull_request, push, workflow_dispatch]
on:
push:
branches: [master]
pull_request:
workflow_dispatch:

jobs:
test:
Expand All @@ -9,7 +13,7 @@ jobs:
fail-fast: true
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
python-version: ["3.12", "3.13"]
python-version: ["3.12", "3.13", "3.14"]

steps:
- name: Checkout source
Expand All @@ -27,5 +31,32 @@ jobs:
pip install -r requirements.txt
pip install -r test/requirements.txt

- name: Run tests
run: pytest

test-legacy:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: ["ubuntu-latest", "windows-latest"]
python-version: ["3.10", "3.11"]

steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Setup python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64

- name: Install
run: |
pip install -ve .[all]
pip install -r requirements.txt
pip install -r test/requirements.txt

- name: Run tests
run: pytest
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ MANIFEST
bench/
pypi/
caterpillarapi.h
.python-version
.benchmarks/

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
# Changelog

## [2.9.0] - Python 3.14 Compatibility and Generic Templates

### Added

- Python 3.14-compatible conditional field metadata:
`with If(condition) as when:`, `f[..., when]`, `Start(when)` /
`End(when)`, explicit `ElseIf(previous, condition)` chains, and
`Else(previous)` fallback branches.
- `Branch`, `When`, and `Otherwise` for conditional variants of one attribute,
including branch-local `f[...]` options such as byte order.
- Python generic templates based on normal `typing.TypeVar` and `Generic`
classes. Templates now support direct `Template[uint8]` specialization,
partial specialization, specialization caching, and runtime `__origin__` /
`__args__` metadata.
- `field_of()` for carrying generic template field metadata through `f[...]`,
including sequence length, offset, switch options, byte order, bit width, and
conditions.
- `Padded`, `PrePad`, and `PostPad` wrappers for explicit before/after padding,
dynamic padding lengths, repeated fill patterns, strict validation, and slash
syntax such as `uint8 / PrePad(1) / PostPad(2)`.
- A `pytest-benchmark` benchmark suite covering the `examples/comparison` workload.

### Changes

- `derive()` now handles generic templates, returns already materialized struct
classes unchanged when no type arguments are supplied, supports keyword
defaults in legacy templates, and evaluates deferred annotations while legacy
template variables are still available.
- Native C atoms (`Repeated`, `Conditional`, `AtOffset`, and `Switch`) now accept
Python struct classes and other `__struct__` containers, improving Python/C
interoperability and `c_Context` coverage.
- `PyStructFormattedField` now handles prefixed sequence unpacking and validates
empty sequence packing after processing fixed or prefixed lengths.
- `Lazy` now resolves objects that expose `__struct__`, allowing lazy factories
to return decorated model classes or struct containers.

### Fixes

- `Field` rejects negative sequence lengths at definition time and uses a faster
no-option unpack path while preserving default fallback and exception wrapping
behavior.
- `Compressed` copies compression and decompression keyword arguments before
resolving context lambdas, preventing cross-call mutation.
- Fixed-length `CString` unpacking now performs exact reads and rejects
truncated input.
- Padding validation now covers strict greedy fill patterns and rejects
unsupported prefixed padding lengths.
- C extension fixes include safer reference handling and error propagation,
length-info forwarding to `__pack_many__` / `__unpack_many__`, `__bits__`
lookup on Python atoms, repeated atom unpack detection, `AtOffset.keep_pos`
behavior, switch type aggregation, arch/endian rich comparison errors, and
writable offset/whence setters.


## [2.8.0] - Extended Syntax

### Added
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ assert data_le != data_be
```

> [!NOTE]
> Python 3.14 breaks `with` statements in class definitions since `__annotations__` are added at the end
> of a class definition. Therefore, `Digest` and conditional statements **ARE NOT SUPPORTED** using the `with` syntax in Python 3.14+.
> As of version `2.4.5` the `Digest` class has a counterpart (`DigestField`), which can be used to manually specify a digest without
> the need of a `ẁith` statement.
> Python 3.14 changes when class-body `__annotations__` are available. Digest context managers still need the explicit
> `DigestField` form on Python 3.14+, but conditional `with` blocks are supported through explicit metadata:
> `with If(condition) as when:` plus `f[..., when]` for one field or `Start(when)` / `End(when)` for a block.
> For conditional variants of one attribute, use `Branch(When(...), Otherwise(...))`.

This library offers extensive functionality beyond basic struct definitions. For further details
on its powerful features, explore the official [documentation](https://matrixeditor.github.io/caterpillar/),
Expand Down Expand Up @@ -177,4 +177,4 @@ to these approaches.

## License

Distributed under the GNU General Public License (V3). See [License](LICENSE) for more information.
Distributed under the GNU General Public License (V3). See [License](LICENSE) for more information.
Loading
Loading