diff --git a/src/py/lib/test_twine.py b/src/py/lib/test_twine.py index ae21076d..d4644683 100644 --- a/src/py/lib/test_twine.py +++ b/src/py/lib/test_twine.py @@ -9,6 +9,16 @@ def test_leb128(): c = twine.Decoder(b'\x81\x42') assert c._leb128(off=0)[0] == ((0x42 << 7) + 1) # pyright: ignore[reportPrivateUsage] +def test_caches_are_per_instance(): + # Regression: `Decoder.caches` must not be shared across instances, otherwise + # cached values keyed by offset leak from one artifact's decoder into another's, + # returning stale decoded sub-values at colliding offsets. + d1 = twine.Decoder(b'\x00') + d2 = twine.Decoder(b'\x00') + assert d1.caches is not d2.caches + d1.caches.setdefault('T', {})[42] = 'stale' + assert 42 not in d2.caches.get('T', {}) + def _get_testdata1() -> twine.Decoder: with open('test_data/typereg.twine', 'rb') as f: data = bytearray(f.read()) diff --git a/src/py/lib/twine.py b/src/py/lib/twine.py index 25ea94b4..68d753b6 100644 --- a/src/py/lib/twine.py +++ b/src/py/lib/twine.py @@ -17,12 +17,13 @@ class Error(Exception): class Decoder: """A twine decoder""" - # per-type cache - caches: dict[str, dict[offset, Any]] = {} + # per-type cache, keyed by offset; must be per-instance (per-artifact), + caches: dict[str, dict[offset, Any]] def __init__(self, a: bytearray | bytes): a = bytearray(a) self.bs = a + self.caches = {} def __first_byte(self, off: int) -> tuple[int, int]: c = self.bs[off] diff --git a/src/py/pyproject.toml b/src/py/pyproject.toml index 7e0bb0fe..563bc4d6 100644 --- a/src/py/pyproject.toml +++ b/src/py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "imandrax_api" -version = "0.20" +version = "0.20.1" description = "Imandrax API client library" requires-python = ">=3.12" dependencies = [ diff --git a/src/py/setup.py b/src/py/setup.py index 0d3f0cca..a05f6ae6 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -VERSION = "0.20" +VERSION = "0.20.1" setup( name="imandrax_api", version=VERSION, diff --git a/src/py/uv.lock b/src/py/uv.lock index 5928b302..73f63b44 100644 --- a/src/py/uv.lock +++ b/src/py/uv.lock @@ -284,7 +284,7 @@ wheels = [ [[package]] name = "imandrax-api" -version = "0.20" +version = "0.20.1" source = { editable = "." } dependencies = [ { name = "protobuf" },