Skip to content

Commit 0891d7e

Browse files
committed
Fix history export serialization quirk
1 parent 9db2da2 commit 0891d7e

4 files changed

Lines changed: 20 additions & 34 deletions

File tree

durabletask/extensions/history_export/client.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252

5353
from __future__ import annotations
5454

55-
import json
5655
import time
5756
import uuid
5857
from collections.abc import Iterator
@@ -221,12 +220,8 @@ def get_job(self, job_id: str) -> ExportJobDescription | None:
221220
meta = self._client.get_entity(entity_id, include_state=True)
222221
if meta is None:
223222
return None
224-
raw = meta.get_state(str)
225-
if not raw:
226-
return None
227-
try:
228-
state = json.loads(raw)
229-
except (TypeError, ValueError):
223+
state = meta.get_state()
224+
if not state:
230225
return None
231226
if not isinstance(state, dict):
232227
return None
@@ -265,31 +260,24 @@ def list_jobs(
265260
# explicit entity-name check.
266261
if meta.id.entity != ENTITY_NAME.lower():
267262
continue
268-
raw = meta.get_state(str)
263+
raw = meta.get_state()
269264
if not raw:
270265
logger.warning(
271266
"list_jobs: skipping export-job entity %r with no "
272267
"persisted state", meta.id.key,
273268
)
274269
continue
275-
try:
276-
state = json.loads(raw)
277-
except (TypeError, ValueError) as ex:
278-
logger.warning(
279-
"list_jobs: skipping export-job entity %r; failed to "
280-
"parse state JSON (%s)", meta.id.key, ex,
281-
)
282-
continue
283-
if not isinstance(state, dict):
270+
if not isinstance(raw, dict):
284271
logger.warning(
285272
"list_jobs: skipping export-job entity %r; persisted "
286273
"state is not a JSON object (got %s)",
287-
meta.id.key, type(state).__name__,
274+
meta.id.key, type(raw).__name__,
288275
)
289276
continue
277+
state = cast("dict[str, Any]", raw)
290278
try:
291279
desc = ExportJobDescription.from_state_dict(
292-
meta.id.key, cast("dict[str, Any]", state),
280+
meta.id.key, state,
293281
)
294282
except (KeyError, ValueError) as ex:
295283
logger.warning(

tests/durabletask/extensions/history_export/test_entity.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from __future__ import annotations
1212

13-
import json
1413
from datetime import datetime, timezone
1514
from typing import Callable, Optional
1615

@@ -88,9 +87,9 @@ def _create_payload() -> dict:
8887

8988

9089
def _state_dict(metadata) -> dict:
91-
raw = metadata.get_state(str)
92-
assert raw is not None
93-
return json.loads(raw)
90+
state = metadata.get_state()
91+
assert isinstance(state, dict)
92+
return state
9493

9594

9695
def _wait_for_state(
@@ -106,12 +105,8 @@ def _check() -> Optional[dict]:
106105
meta = c.get_entity(entity_id, include_state=True)
107106
if meta is None:
108107
return None
109-
raw = meta.get_state(str)
110-
if not raw:
111-
return None
112-
try:
113-
state = json.loads(raw)
114-
except (TypeError, ValueError):
108+
state = meta.get_state()
109+
if not isinstance(state, dict):
115110
return None
116111
return state if predicate(state) else None
117112

tests/durabletask/test_data_converter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ def serialize(self, value: Any) -> str | None:
8484
def deserialize(self, data: str | None, target_type: type | None = None) -> Any:
8585
return None if data is None else json.loads(data)
8686

87+
def coerce(self, value: Any, target_type: type | None = None) -> Any:
88+
return value
89+
8790
conv = UpperConverter()
8891
assert conv.serialize("hello") == '"HELLO"'
8992
assert conv.deserialize('"HELLO"') == "HELLO"
93+
assert conv.coerce("HELLO") == "HELLO"

tests/durabletask/test_entity_executor.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,8 @@ def from_json(cls, data):
177177
assert isinstance(result, Wrapped)
178178
assert result.n == 3
179179

180-
def test_get_state_invalid_coercion_raises_typeerror(self):
181-
import pytest
182-
180+
def test_get_state_invalid_coercion_falls_back_to_raw(self):
181+
# The default converter is best-effort: a coercion failure returns the
182+
# raw value rather than raising.
183183
state = StateShim("not-an-int")
184-
with pytest.raises(TypeError):
185-
state.get_state(int)
184+
assert state.get_state(int) == "not-an-int"

0 commit comments

Comments
 (0)