Skip to content

Commit 582e86f

Browse files
author
Hamish Downer
committed
Properly handle InfeasibleQuotasError in serialize/deserialize
1 parent 1cc3d3a commit 582e86f

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

src/sortition_algorithms/features.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
MAX_FLEX_UNSET = -1
4545

4646

47-
@define(kw_only=True, slots=True)
47+
@define(kw_only=True, slots=True, eq=True)
4848
class FeatureValueMinMax:
4949
min: int
5050
max: int

src/sortition_algorithms/utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def _unstructure_exception(exc: Exception) -> dict[str, Any]:
126126
result["all_errors"] = _converter.unstructure(exc.all_errors)
127127
if hasattr(exc, "features"):
128128
# Don't serialize features as it's complex and circular
129-
result["has_features"] = True
129+
result["features"] = _converter.unstructure(exc.features)
130130
return result
131131

132132

@@ -148,9 +148,14 @@ def _structure_exception(obj: dict[str, Any], _: Any) -> Exception:
148148
)
149149
return errors.ParseTableMultiError(all_errors)
150150
elif exc_type_name == "sortition_algorithms.errors.InfeasibleQuotasError":
151+
# avoid circular import
152+
from sortition_algorithms.features import FeatureCollection
153+
154+
features = _converter.structure(obj.get("features", {}), FeatureCollection)
155+
output = _converter.structure(obj.get("all_lines", ["dummy"]), list[str])[1:]
151156
# We can't fully reconstruct this as it needs a FeatureCollection
152157
# Just create a basic SelectionMultilineError with the lines if available
153-
return errors.SelectionMultilineError(obj.get("all_lines", obj.get("args", ["Infeasible quotas error"])))
158+
return errors.InfeasibleQuotasError(features=features, output=output)
154159
elif exc_type_name.startswith("sortition_algorithms.errors."):
155160
# For other custom errors, try to find the class
156161
class_name = exc_type_name.split(".")[-1]

tests/test_utils.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import pytest
77

8-
from sortition_algorithms.errors import SelectionError, SelectionMultilineError
8+
from sortition_algorithms.errors import InfeasibleQuotasError, SelectionError, SelectionMultilineError
9+
from sortition_algorithms.features import FeatureValueMinMax
910
from sortition_algorithms.utils import ReportLevel, RunReport, get_cell_name
1011

1112

@@ -506,6 +507,32 @@ def test_run_report_with_multiline_error_deserialisation(self):
506507
assert str(deserialised_report.last_error()) == "Error line 1\nError line 2\nError line 3"
507508
assert isinstance(deserialised_report.last_error(), SelectionMultilineError)
508509

510+
def test_run_report_with_infeasible_quotas_error_serialisation(self):
511+
report = RunReport()
512+
report.add_error(
513+
InfeasibleQuotasError(
514+
features={"feat1": {"value1": FeatureValueMinMax(min=2, max=4)}}, output=["line1", "line2"]
515+
)
516+
)
517+
serialised_form = report.serialize()
518+
assert "_data" in serialised_form
519+
assert len(serialised_form["_data"]) == 1
520+
521+
def test_run_report_with_infeasible_quotas_error_deserialisation(self):
522+
report = RunReport()
523+
error = InfeasibleQuotasError(
524+
features={"feat1": {"value1": FeatureValueMinMax(min=2, max=4)}}, output=["line1", "line2"]
525+
)
526+
report.add_error(error)
527+
serialised_form = report.serialize()
528+
deserialised_report = RunReport.deserialize(serialised_form)
529+
530+
# Check the error is preserved with all lines and features
531+
deserialised_error = deserialised_report.last_error()
532+
assert isinstance(deserialised_error, InfeasibleQuotasError)
533+
assert str(deserialised_error) == "The quotas are infeasible:\nline1\nline2"
534+
assert deserialised_error.features == {"feat1": {"value1": FeatureValueMinMax(min=2, max=4)}}
535+
509536
def test_run_report_with_mixed_content_serialisation_deserialisation(self):
510537
"""Test round-trip with lines, tables, and errors mixed together"""
511538
report = RunReport()

0 commit comments

Comments
 (0)