Skip to content

Commit 80e125e

Browse files
committed
🐛 Restore ArrayData array serialization helpers (#7480)
Restore the public `ArrayData.save_arrays()` and `ArrayData.load_arrays()` helpers. These utilities are self-contained compatibility helpers for serializing numpy arrays to base64-encoded `.npy` payloads and loading them back. Add regression coverage for the roundtrip.
1 parent 52f590b commit 80e125e

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

src/aiida/orm/nodes/data/array/array.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
from __future__ import annotations
1212

13+
import base64
14+
import io
1315
from collections.abc import Iterable, Iterator, Sequence
1416
from typing import Any, BinaryIO
1517

@@ -116,6 +118,55 @@ def __init__(self, arrays: Iterable | dict[str, Iterable] | None = None, **kwarg
116118
for key, value in arrays.items():
117119
self.set_array(key, np.asarray(value))
118120

121+
@staticmethod
122+
def save_arrays(arrays: dict[str, np.ndarray]) -> dict[str, bytes]:
123+
"""Serialize arrays to base64-encoded ``.npy`` payloads.
124+
125+
:param arrays: Mapping of array names to numpy arrays.
126+
:return: Mapping of array names to base64-encoded bytes.
127+
"""
128+
from aiida.common.warnings import warn_deprecation
129+
130+
warn_deprecation(
131+
'`ArrayData.save_arrays` is deprecated. Use `numpy.save` with `io.BytesIO` directly instead.',
132+
version=3,
133+
stacklevel=2,
134+
)
135+
136+
results = {}
137+
138+
for key, array in arrays.items():
139+
stream = io.BytesIO()
140+
np.save(stream, array, allow_pickle=False)
141+
stream.seek(0)
142+
results[key] = base64.encodebytes(stream.read())
143+
144+
return results
145+
146+
@staticmethod
147+
def load_arrays(arrays: dict[str, bytes]) -> dict[str, np.ndarray]:
148+
"""Deserialize arrays from base64-encoded ``.npy`` payloads.
149+
150+
:param arrays: Mapping of array names to base64-encoded bytes.
151+
:return: Mapping of array names to numpy arrays.
152+
"""
153+
from aiida.common.warnings import warn_deprecation
154+
155+
warn_deprecation(
156+
'`ArrayData.load_arrays` is deprecated. Use `numpy.load` with `io.BytesIO` directly instead.',
157+
version=3,
158+
stacklevel=2,
159+
)
160+
161+
results = {}
162+
163+
for key, encoded in arrays.items():
164+
stream = io.BytesIO(base64.decodebytes(encoded))
165+
stream.seek(0)
166+
results[key] = np.load(stream, allow_pickle=False)
167+
168+
return results
169+
119170
@property
120171
def arrays(self) -> dict[str, np.ndarray]:
121172
return {name: self.get_array(name) for name in self.get_arraynames()}

tests/orm/nodes/data/test_array.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import numpy
1212
import pytest
1313

14+
from aiida.common.warnings import AiidaDeprecationWarning
1415
from aiida.orm import ArrayData, load_node
1516

1617

@@ -61,3 +62,22 @@ def test_get_array():
6162

6263
node = ArrayData(numpy.array([1, 2]))
6364
assert (node.get_array() == numpy.array([1, 2])).all()
65+
66+
67+
def test_save_and_load_arrays():
68+
"""Test :meth:`aiida.orm.ArrayData.save_arrays` and ``load_arrays``."""
69+
arrays = {
70+
'a': numpy.array([1, 2, 3]),
71+
'b': numpy.array([[1.0, 2.0], [3.0, 4.0]]),
72+
}
73+
74+
with pytest.warns(AiidaDeprecationWarning, match='ArrayData.save_arrays'):
75+
serialized = ArrayData.save_arrays(arrays)
76+
assert set(serialized) == {'a', 'b'}
77+
assert all(isinstance(value, bytes) for value in serialized.values())
78+
79+
with pytest.warns(AiidaDeprecationWarning, match='ArrayData.load_arrays'):
80+
deserialized = ArrayData.load_arrays(serialized)
81+
assert set(deserialized) == {'a', 'b'}
82+
assert numpy.array_equal(deserialized['a'], arrays['a'])
83+
assert numpy.array_equal(deserialized['b'], arrays['b'])

0 commit comments

Comments
 (0)