Skip to content

Commit 108b934

Browse files
feat: remove msgpack
Signed-off-by: jaapschoutenalliander <[email protected]>
1 parent b77f8e3 commit 108b934

File tree

6 files changed

+20
-261
lines changed

6 files changed

+20
-261
lines changed

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ dependencies = [
3030
"power-grid-model>=1.7",
3131
"rustworkx>= 0.15.1",
3232
"numpy>=2.0",
33-
"msgpack>=1.1.2",
3433
]
3534
dynamic = ["version"]
3635

src/power_grid_model_ds/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@
77
from power_grid_model_ds._core.model.grids.base import Grid
88
from power_grid_model_ds._core.utils.serialization import (
99
load_grid_from_json,
10-
load_grid_from_msgpack,
1110
save_grid_to_json,
12-
save_grid_to_msgpack,
1311
)
1412

1513
__all__ = [
1614
"Grid",
1715
"GraphContainer",
1816
"PowerGridModelInterface",
1917
"save_grid_to_json",
20-
"save_grid_to_msgpack",
2118
"load_grid_from_json",
22-
"load_grid_from_msgpack",
2319
]

src/power_grid_model_ds/_core/model/grids/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ def get_downstream_nodes(self, node_id: int, inclusive: bool = False):
362362
def cache(self, cache_dir: Path, cache_name: str, compress: bool = True):
363363
"""Cache Grid to a folder using pickle format.
364364
365-
Note: Consider using save_to_json() or save_to_msgpack() for better
365+
Note: Consider using save_to_json() for better
366366
interoperability and standardized format.
367367
368368
Args:

src/power_grid_model_ds/_core/utils/serialization.py

Lines changed: 2 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
import logging
1010
from ast import literal_eval
1111
from pathlib import Path
12-
from typing import Dict, Literal, Optional
12+
from typing import Dict, Optional
1313

14-
import msgpack
1514
import numpy as np
16-
from power_grid_model.utils import json_deserialize, json_serialize, msgpack_deserialize, msgpack_serialize
15+
from power_grid_model.utils import json_deserialize, json_serialize
1716

1817
from power_grid_model_ds._core.load_flow import PGM_ARRAYS, PowerGridModelInterface
1918
from power_grid_model_ds._core.model.arrays.base.array import FancyArray
@@ -187,60 +186,6 @@ def _create_grid_from_input_data(input_data: Dict, target_grid_class=None):
187186
return interface.create_grid_from_input_data()
188187

189188

190-
def _extract_msgpack_data(data: bytes, **kwargs):
191-
"""Extract input data and extensions from MessagePack data."""
192-
try:
193-
data_dict = msgpack.unpackb(data, raw=False)
194-
if isinstance(data_dict, dict) and EXTENSIONS_KEY in data_dict:
195-
# Extract extensions and deserialize core data
196-
extensions = data_dict.pop(EXTENSIONS_KEY, {})
197-
core_data = msgpack.packb(data_dict)
198-
input_data = msgpack_deserialize(core_data, **kwargs)
199-
else:
200-
# No extensions, use power-grid-model directly
201-
input_data = msgpack_deserialize(data, **kwargs)
202-
extensions = {EXTENDED_COLUMNS_KEY: {}, CUSTOM_ARRAYS_KEY: {}}
203-
except (msgpack.exceptions.ExtraData, ValueError, TypeError) as e:
204-
# Handle MessagePack parsing failures:
205-
# - ExtraData: malformed MessagePack data
206-
# - ValueError/TypeError: invalid data structure or type issues
207-
logger.warning(f"Failed to extract extensions from MessagePack data: {e}")
208-
input_data = msgpack_deserialize(data, **kwargs)
209-
extensions = {EXTENDED_COLUMNS_KEY: {}, CUSTOM_ARRAYS_KEY: {}}
210-
211-
return input_data, extensions
212-
213-
214-
def _get_serialization_path(path: Path, format_type: Literal["json", "msgpack", "auto"] = "auto") -> Path:
215-
"""Get the correct path for serialization format.
216-
217-
Args:
218-
path: Base path
219-
format_type: "json", "msgpack", or "auto" to detect from extension
220-
221-
Returns:
222-
Path: Path with correct extension
223-
"""
224-
json_extensions = [".json"]
225-
msgpack_extensions = [".msgpack", ".mp"]
226-
227-
if format_type == "auto":
228-
if path.suffix.lower() in json_extensions:
229-
format_type = "json"
230-
elif path.suffix.lower() in msgpack_extensions:
231-
format_type = "msgpack"
232-
else:
233-
# Default to JSON
234-
format_type = "json"
235-
236-
if format_type == "json" and path.suffix.lower() != json_extensions[0]:
237-
return path.with_suffix(json_extensions[0])
238-
if format_type == "msgpack" and path.suffix.lower() not in msgpack_extensions:
239-
return path.with_suffix(msgpack_extensions[0])
240-
241-
return path
242-
243-
244189
def save_grid_to_json(
245190
grid,
246191
path: Path,
@@ -303,65 +248,3 @@ def load_grid_from_json(path: Path, target_grid_class=None):
303248
_restore_extensions_data(grid, extensions)
304249

305250
return grid
306-
307-
308-
def save_grid_to_msgpack(grid, path: Path, use_compact_list: bool = True, preserve_extensions: bool = True) -> Path:
309-
"""Save a Grid object to MessagePack format with extensions support.
310-
311-
Args:
312-
grid: The Grid object to serialize
313-
path: The file path to save to
314-
use_compact_list: Whether to use compact list format
315-
preserve_extensions: Whether to save extended columns and custom arrays
316-
317-
Returns:
318-
Path: The path where the file was saved
319-
"""
320-
path.parent.mkdir(parents=True, exist_ok=True)
321-
322-
# Convert Grid to power-grid-model input format and serialize
323-
interface = PowerGridModelInterface(grid=grid)
324-
input_data = interface.create_input_from_grid()
325-
326-
core_data = msgpack_serialize(input_data, use_compact_list=use_compact_list)
327-
328-
# Add extensions if requested (requires re-serialization for MessagePack)
329-
if preserve_extensions:
330-
extensions = _extract_extensions_data(grid)
331-
if extensions[EXTENDED_COLUMNS_KEY] or extensions[CUSTOM_ARRAYS_KEY]:
332-
core_dict = msgpack.unpackb(core_data, raw=False)
333-
core_dict[EXTENSIONS_KEY] = extensions
334-
serialized_data = msgpack.packb(core_dict)
335-
else:
336-
serialized_data = core_data
337-
else:
338-
serialized_data = core_data
339-
340-
# Write to file
341-
with open(path, "wb") as f:
342-
f.write(serialized_data)
343-
344-
return path
345-
346-
347-
def load_grid_from_msgpack(path: Path, target_grid_class=None):
348-
"""Load a Grid object from MessagePack format with cross-type loading support.
349-
350-
Args:
351-
path: The file path to load from
352-
target_grid_class: Optional Grid class to load into. If None, uses default Grid.
353-
354-
Returns:
355-
Grid: The deserialized Grid object of the specified target class
356-
"""
357-
with open(path, "rb") as f:
358-
data = f.read()
359-
360-
# Extract extensions and deserialize core data
361-
input_data, extensions = _extract_msgpack_data(data)
362-
363-
# Create grid and restore extensions
364-
grid = _create_grid_from_input_data(input_data, target_grid_class)
365-
_restore_extensions_data(grid, extensions)
366-
367-
return grid

tests/unit/utils/test_serialization.py

Lines changed: 17 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from dataclasses import dataclass
99
from pathlib import Path
1010
from tempfile import TemporaryDirectory
11-
from typing import Literal
1211

1312
import numpy as np
1413
import pytest
@@ -18,12 +17,9 @@
1817
from power_grid_model_ds._core.model.arrays.base.array import FancyArray
1918
from power_grid_model_ds._core.utils.serialization import (
2019
_extract_extensions_data,
21-
_get_serialization_path,
2220
_restore_extensions_data,
2321
load_grid_from_json,
24-
load_grid_from_msgpack,
2522
save_grid_to_json,
26-
save_grid_to_msgpack,
2723
)
2824
from power_grid_model_ds.arrays import LineArray
2925
from power_grid_model_ds.arrays import NodeArray as BaseNodeArray
@@ -95,46 +91,25 @@ def extended_grid():
9591
class TestSerializationFormats:
9692
"""Test serialization across different formats and configurations"""
9793

98-
@pytest.mark.parametrize(
99-
"format_type,preserve_ext", [("json", True), ("json", False), ("msgpack", True), ("msgpack", False)]
100-
)
101-
def test_basic_serialization_roundtrip(
102-
self, basic_grid: Grid, temp_dir: Path, format_type: str, preserve_ext: bool
103-
):
94+
@pytest.mark.parametrize("preserve_ext", [(True), (False)])
95+
def test_basic_serialization_roundtrip(self, basic_grid: Grid, temp_dir: Path, preserve_ext: bool):
10496
"""Test basic serialization roundtrip for all formats"""
105-
ext = "json" if format_type == "json" else "msgpack"
106-
path = temp_dir / f"test.{ext}"
107-
108-
# Save
109-
if format_type == "json":
110-
result_path = save_grid_to_json(basic_grid, path, preserve_extensions=preserve_ext)
111-
else:
112-
result_path = save_grid_to_msgpack(basic_grid, path, preserve_extensions=preserve_ext)
113-
97+
path = temp_dir / "test.json"
98+
result_path = save_grid_to_json(basic_grid, path, preserve_extensions=preserve_ext)
11499
assert result_path.exists()
115100

116101
# Load and verify
117-
if format_type == "json":
118-
loaded_grid = load_grid_from_json(path, target_grid_class=Grid)
119-
else:
120-
loaded_grid = load_grid_from_msgpack(path, target_grid_class=Grid)
102+
loaded_grid = load_grid_from_json(path, target_grid_class=Grid)
121103
assert loaded_grid.node.size == basic_grid.node.size
122104
assert loaded_grid.line.size == basic_grid.line.size
123105
assert list(loaded_grid.node.id) == list(basic_grid.node.id)
124106

125-
@pytest.mark.parametrize("format_type", ["json", "msgpack"])
126-
def test_extended_serialization_roundtrip(self, extended_grid: ExtendedGrid, temp_dir: Path, format_type: str):
107+
def test_extended_serialization_roundtrip(self, extended_grid: ExtendedGrid, temp_dir: Path):
127108
"""Test extended serialization preserving custom data"""
128-
ext = "json" if format_type == "json" else "msgpack"
129-
path = temp_dir / f"extended.{ext}"
109+
path = temp_dir / "extended.json"
130110

131-
# Save with extensions
132-
if format_type == "json":
133-
save_grid_to_json(extended_grid, path, preserve_extensions=True)
134-
loaded_grid = load_grid_from_json(path, target_grid_class=ExtendedGrid)
135-
else:
136-
save_grid_to_msgpack(extended_grid, path, preserve_extensions=True)
137-
loaded_grid = load_grid_from_msgpack(path, target_grid_class=ExtendedGrid)
111+
save_grid_to_json(extended_grid, path, preserve_extensions=True)
112+
loaded_grid = load_grid_from_json(path, target_grid_class=ExtendedGrid)
138113

139114
# Verify core data
140115
assert loaded_grid.node.size == extended_grid.node.size
@@ -148,37 +123,25 @@ def test_extended_serialization_roundtrip(self, extended_grid: ExtendedGrid, tem
148123
class TestCrossTypeCompatibility:
149124
"""Test cross-type loading and compatibility"""
150125

151-
@pytest.mark.parametrize("format_type", ["json", "msgpack"])
152-
def test_basic_to_extended_loading(self, basic_grid: Grid, temp_dir: Path, format_type: str):
126+
def test_basic_to_extended_loading(self, basic_grid: Grid, temp_dir: Path):
153127
"""Test loading basic grid into extended type"""
154-
ext = "json" if format_type == "json" else "msgpack"
155-
path = temp_dir / f"basic.{ext}"
128+
path = temp_dir / "basic.json"
156129

157130
# Save basic grid
158-
if format_type == "json":
159-
save_grid_to_json(basic_grid, path)
160-
loaded_grid = load_grid_from_json(path, target_grid_class=ExtendedGrid)
161-
else:
162-
save_grid_to_msgpack(basic_grid, path)
163-
loaded_grid = load_grid_from_msgpack(path, target_grid_class=ExtendedGrid)
131+
save_grid_to_json(basic_grid, path)
132+
loaded_grid = load_grid_from_json(path, target_grid_class=ExtendedGrid)
164133

165134
# Core data should transfer
166135
assert loaded_grid.node.size == basic_grid.node.size
167136
assert loaded_grid.line.size == basic_grid.line.size
168137

169-
@pytest.mark.parametrize("format_type", ["json", "msgpack"])
170-
def test_extended_to_basic_loading(self, extended_grid: ExtendedGrid, temp_dir: Path, format_type: str):
138+
def test_extended_to_basic_loading(self, extended_grid: ExtendedGrid, temp_dir: Path):
171139
"""Test loading extended grid into basic type"""
172-
ext = "json" if format_type == "json" else "msgpack"
173-
path = temp_dir / f"extended.{ext}"
140+
path = temp_dir / "extended.json"
174141

175142
# Save extended grid
176-
if format_type == "json":
177-
save_grid_to_json(extended_grid, path, preserve_extensions=True)
178-
loaded_grid = load_grid_from_json(path, target_grid_class=Grid)
179-
else:
180-
save_grid_to_msgpack(extended_grid, path, preserve_extensions=True)
181-
loaded_grid = load_grid_from_msgpack(path, target_grid_class=Grid)
143+
save_grid_to_json(extended_grid, path, preserve_extensions=True)
144+
loaded_grid = load_grid_from_json(path, target_grid_class=Grid)
182145

183146
# Core data should transfer
184147
assert loaded_grid.node.size == extended_grid.node.size
@@ -251,46 +214,6 @@ class GridWithCustomArray(Grid):
251214
np.testing.assert_array_almost_equal(loaded_grid.custom_metadata.metadata_value, [1.5, 2.5, 3.5])
252215
np.testing.assert_array_equal(loaded_grid.custom_metadata.category, [1, 2, 1])
253216

254-
# Test MessagePack serialization
255-
msgpack_path = temp_dir / "custom_array.msgpack"
256-
save_grid_to_msgpack(grid, msgpack_path, preserve_extensions=True)
257-
258-
# Load back and verify
259-
loaded_grid_mp = load_grid_from_msgpack(msgpack_path, target_grid_class=GridWithCustomArray)
260-
261-
# Verify core data
262-
assert loaded_grid_mp.node.size == 2
263-
np.testing.assert_array_equal(loaded_grid_mp.node.id, [1, 2])
264-
265-
# Verify custom array was preserved
266-
assert hasattr(loaded_grid_mp, "custom_metadata")
267-
assert loaded_grid_mp.custom_metadata.size == 3
268-
np.testing.assert_array_equal(loaded_grid_mp.custom_metadata.id, [100, 200, 300])
269-
np.testing.assert_array_almost_equal(loaded_grid_mp.custom_metadata.metadata_value, [1.5, 2.5, 3.5])
270-
np.testing.assert_array_equal(loaded_grid_mp.custom_metadata.category, [1, 2, 1])
271-
272-
273-
class TestUtilityFunctions:
274-
"""Test utility functions and path handling"""
275-
276-
@pytest.mark.parametrize(
277-
"input_path,format_type,expected",
278-
[
279-
("test.json", "auto", "test.json"),
280-
("test.msgpack", "auto", "test.msgpack"),
281-
("test.mp", "auto", "test.mp"),
282-
("test.xyz", "auto", "test.json"), # Unknown defaults to JSON
283-
("test.xyz", "json", "test.json"),
284-
("test.xyz", "msgpack", "test.msgpack"),
285-
],
286-
)
287-
def test_serialization_path_handling(
288-
self, input_path: str, format_type: Literal["json", "msgpack", "auto"], expected: str
289-
):
290-
"""Test path handling and format detection"""
291-
result = _get_serialization_path(Path(input_path), format_type)
292-
assert result == Path(expected)
293-
294217

295218
class TestSpecialCases:
296219
"""Test special cases and edge scenarios"""
@@ -300,18 +223,13 @@ def test_empty_grid_handling(self, temp_dir: Path):
300223
empty_grid = Grid.empty()
301224

302225
json_path = temp_dir / "empty.json"
303-
msgpack_path = temp_dir / "empty.msgpack"
304226

305227
# Should handle empty grids
306228
save_grid_to_json(empty_grid, json_path)
307-
save_grid_to_msgpack(empty_grid, msgpack_path)
308229

309230
# Should load back as empty
310231
loaded_json = load_grid_from_json(json_path, target_grid_class=Grid)
311-
loaded_msgpack = load_grid_from_msgpack(msgpack_path, target_grid_class=Grid)
312-
313232
assert loaded_json.node.size == 0
314-
assert loaded_msgpack.node.size == 0
315233

316234
def test_custom_array_extraction_edge_cases(self, temp_dir: Path):
317235
"""Test edge cases in custom array extraction"""

0 commit comments

Comments
 (0)