Skip to content

Commit f477b0c

Browse files
authored
fix(io): deserialize parametrized Decimal dtypes from yaml/json (#2434)
* fix(io): deserialize parametrized Decimal dtypes from yaml/json Signed-off-by: Sebastian Cao <cycsmail@gmail.com> * match Decimal pattern before the engine fallback Signed-off-by: Sebastian Cao <cycsmail@gmail.com> --------- Signed-off-by: Sebastian Cao <cycsmail@gmail.com>
1 parent 85cc2a1 commit f477b0c

3 files changed

Lines changed: 52 additions & 3 deletions

File tree

pandera/engines/pandas_engine.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,10 @@ class Decimal(DataType, dtypes.Decimal):
577577
supported by the Python :py:class:`decimal.Decimal` class.
578578
"""
579579

580-
_exp: decimal.Decimal = dataclasses.field(init=False)
581-
_ctx: decimal.Context = dataclasses.field(init=False)
580+
# attributes derived from precision, scale, and rounding, so they are
581+
# excluded from comparisons: decimal.Context doesn't implement __eq__.
582+
_exp: decimal.Decimal = dataclasses.field(init=False, compare=False)
583+
_ctx: decimal.Context = dataclasses.field(init=False, compare=False)
582584

583585
def __init__(
584586
self,

pandera/io/pandas_io.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import enum
66
import json
7+
import re
78
import warnings
89
from collections.abc import Mapping
910
from functools import partial
@@ -311,13 +312,37 @@ def handle_stat_dtype(stat):
311312
return check_instance
312313

313314

315+
_DECIMAL_DTYPE_PATTERN = re.compile(
316+
r"Decimal\(\s*(\d+)\s*,\s*(\d+)\s*\)", re.IGNORECASE
317+
)
318+
319+
320+
def _deserialize_dtype(serialized_dtype):
321+
"""Deserialize a dtype, supporting the string representation of
322+
parametrized dtypes that the engine doesn't recognize as aliases,
323+
e.g. ``"Decimal(28, 0)"``. GH#1165
324+
325+
Parametrized dtype strings are handled before falling back to
326+
:meth:`Engine.dtype`, since the exception raised for unrecognized
327+
strings varies across numpy/pandas versions (e.g. numpy raises
328+
``ValueError`` for strings containing commas).
329+
"""
330+
if isinstance(serialized_dtype, str):
331+
match = _DECIMAL_DTYPE_PATTERN.fullmatch(serialized_dtype)
332+
if match:
333+
return pandas_engine.Decimal(
334+
precision=int(match.group(1)), scale=int(match.group(2))
335+
)
336+
return pandas_engine.Engine.dtype(serialized_dtype)
337+
338+
314339
def _deserialize_component_stats(serialized_component_stats):
315340
serialized_component_stats = dict(serialized_component_stats)
316341
unflatten_component_checks_dict(serialized_component_stats)
317342

318343
dtype = serialized_component_stats.get("dtype")
319344
if dtype:
320-
dtype = pandas_engine.Engine.dtype(dtype)
345+
dtype = _deserialize_dtype(dtype)
321346

322347
description = serialized_component_stats.get("description")
323348
title = serialized_component_stats.get("title")

tests/io/test_pandas_io.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,28 @@ def test_io_yaml_roundtrip_preserves_drop_invalid_rows():
14101410
assert restored == schema
14111411

14121412

1413+
@pytest.mark.skipif(
1414+
SKIP_YAML_TESTS,
1415+
reason="pyyaml >= 5.1.0 required",
1416+
)
1417+
def test_io_roundtrip_decimal_dtype():
1418+
"""Test yaml/json roundtrip of parametrized Decimal dtypes, GH#1165."""
1419+
schema = pandera.DataFrameSchema(
1420+
columns={
1421+
"default": pandera.Column(pandera_base.dtypes.Decimal),
1422+
"parametrized": pandera.Column(pandas_engine.Decimal(10, 2)),
1423+
}
1424+
)
1425+
1426+
restored = schema.from_yaml(schema.to_yaml())
1427+
assert restored.columns["parametrized"].dtype == pandas_engine.Decimal(
1428+
10, 2
1429+
)
1430+
assert restored == schema
1431+
1432+
assert schema.from_json(schema.to_json()) == schema
1433+
1434+
14131435
def test_io_json_with_multiindex_column_labels():
14141436
"""Test JSON serialization for schemas with tuple column labels."""
14151437
import json

0 commit comments

Comments
 (0)