From a7747601de2d50759b08ca661eb074ea9b71e033 Mon Sep 17 00:00:00 2001 From: David Langerman Date: Sat, 18 Jul 2026 15:37:19 -0400 Subject: [PATCH] Fix pickling dltype namedtuples --- dltype/_lib/_core.py | 6 +++++- dltype/tests/dltype_test.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/dltype/_lib/_core.py b/dltype/_lib/_core.py index 01e6140..ed33d9b 100644 --- a/dltype/_lib/_core.py +++ b/dltype/_lib/_core.py @@ -362,6 +362,7 @@ def _inner_dltyped_namedtuple(cls: type[NT]) -> type[NT]: # Create a new __new__ method that validates on construction original_new = cls.__new__ + original_module = inspect.getmodule(cls) def validated_new(cls_inner: type[NT], *args: Any, **kwargs: Any) -> NT: # noqa: ANN401 (these actually can be any type) """A new __new__ method that validates the fields upon construction.""" @@ -385,7 +386,10 @@ def validated_new(cls_inner: type[NT], *args: Any, **kwargs: Any) -> NT: # noqa return instance # Create the new class with our modified __new__ method - return cast("type[NT]", type(cls.__name__, (cls,), {"__new__": validated_new})) + new_cls = type(cls.__name__, (cls,), {"__new__": validated_new}) + new_cls.__module__ = cls.__module__ # Preserve the original module + setattr(original_module, cls.__name__, new_cls) # Update the module's reference to the new class + return cast("type[NT]", new_cls) return _inner_dltyped_namedtuple diff --git a/dltype/tests/dltype_test.py b/dltype/tests/dltype_test.py index 9d8ba99..1b57ee0 100644 --- a/dltype/tests/dltype_test.py +++ b/dltype/tests/dltype_test.py @@ -3,6 +3,7 @@ from __future__ import annotations +import pickle import re import sys import warnings @@ -1787,3 +1788,20 @@ def func(arg: type) -> None: pass func(int) + + +def test_pickle_namedtuple() -> None: + @dltype.dltyped_namedtuple() + class MyNamedTuple(NamedTuple): + tensor: Annotated[torch.Tensor, dltype.FloatTensor["b c h w"]] + mask: Annotated[torch.Tensor, dltype.IntTensor["b h w"]] + other: int + + obj = MyNamedTuple(torch.rand(2, 3, 4, 4), torch.randint(0, 2, (2, 4, 4)), 1) + pickled_obj = pickle.dumps(obj) + unpickled_obj = pickle.loads(pickled_obj) + + assert isinstance(unpickled_obj, MyNamedTuple) + assert torch.allclose(unpickled_obj.tensor, obj.tensor) + assert torch.equal(unpickled_obj.mask, obj.mask) + assert unpickled_obj.other == obj.other