@@ -24,59 +24,44 @@ ADDED
2424- Added a pluggable ` DataConverter ` (` durabletask.serialization ` ) accepted by
2525 ` TaskHubGrpcWorker ` , ` TaskHubGrpcClient ` , and ` AsyncTaskHubGrpcClient ` via a
2626 ` data_converter ` argument. Every payload boundary (inputs, outputs, events,
27- custom status, entity state) routes through it. The default
27+ custom status, entity state) routes through it, so one converter controls how
28+ Python values become JSON and how they are reconstructed. The default
2829 ` JsonDataConverter ` preserves existing behavior, so a custom converter (for
29- example one backed by pydantic) is opt-in. Custom objects can opt in via a
30- ` to_json() ` hook and a ` from_json(value) ` classmethod. Objects that contain
31- other hook-using objects round-trip automatically: nested ` to_json() ` hooks
32- fire at any depth during serialization, and a ` from_json ` hook may declare an
33- optional second parameter (` from_json(cls, value, converter) ` ) to reconstruct
34- nested typed values via ` converter.coerce(child, ChildType) ` instead of by
35- hand.
36- - ` OrchestrationContext.call_activity ` , ` call_sub_orchestrator ` , and
37- ` call_entity ` accept an optional ` return_type ` , and ` wait_for_external_event `
38- accepts an optional ` data_type ` . When provided, the result/event payload is
39- reconstructed as that type (dataclasses — including nested dataclass,
40- ` Optional ` , and ` list ` fields — and ` from_json() ` -capable types) and the
41- returned task is typed accordingly (e.g. ` call_activity(..., return_type=Foo) `
42- yields ` CompletableTask[Foo] ` ). When omitted, the raw deserialized JSON is
43- returned as before.
44- - Inbound payloads are reconstructed from function type annotations. When an
45- orchestrator, activity, or entity operation annotates its input parameter (or
46- an activity its return value) with a dataclass or ` from_json() ` -capable type,
47- the payload is reconstructed as that type. Builtins and unannotated/unknown
48- types are passed through unchanged. An explicit ` return_type ` takes precedence
49- over a discovered annotation.
50- - Added typed accessors to ` client.OrchestrationState ` : ` get_input() ` ,
51- ` get_output() ` , and ` get_custom_status() ` each accept an optional
52- ` expected_type ` and deserialize the corresponding payload, reconstructing
53- dataclasses and ` from_json() ` -capable types. The raw ` serialized_* ` fields are
54- retained.
55- - Objects exposing a ` to_json() ` method are now JSON-serializable when passed as
56- activity/orchestrator inputs or outputs.
57- - Added ` EntityMetadata.get_typed_state(intended_type=...) ` , which deserializes
58- the entity's persisted state and reconstructs dataclasses and
59- ` from_json() ` -capable types. The existing ` get_state() ` is unchanged: with no
60- argument it returns the raw serialized JSON payload, and ` get_state(some_type) `
61- applies constructor-based coercion (` some_type(raw) ` ).
62- - Entity runtime state retrieval (` EntityContext.get_state(intended_type=...) ` /
63- ` DurableEntity.get_state(...) ` ) now also reconstructs dataclasses and
64- ` from_json() ` -capable types, in addition to the existing constructor-based
65- coercion.
30+ example one backed by pydantic) is fully opt-in.
31+ - Custom objects can participate in serialization by exposing a ` to_json() `
32+ method and a ` from_json(value) ` classmethod. Both are honored recursively, so
33+ nested custom objects round-trip through their own hooks.
34+ - Payloads are reconstructed into a caller-supplied type — dataclasses
35+ (including nested fields), ` from_json() ` -capable types, and ` enum.Enum `
36+ members, recursing through ` list ` , ` dict ` , ` tuple ` , and ` Optional ` /` Union `
37+ hints. The type comes from a function's annotations, from an explicit
38+ ` return_type ` on ` call_activity ` / ` call_sub_orchestrator ` / ` call_entity `
39+ (or ` data_type ` on ` wait_for_external_event ` ), or from the typed accessors
40+ ` get_input() ` / ` get_output() ` / ` get_custom_status() ` on
41+ ` client.OrchestrationState ` and ` EntityMetadata.get_typed_state(...) ` . It is
42+ never inferred from the payload. Which annotated types are eligible is decided
43+ by the converter via the overridable ` DataConverter.can_reconstruct(...) ` ; a
44+ custom converter can override it to recognize its own types (for example
45+ ` pydantic.BaseModel ` subclasses).
6646
6747CHANGED
6848
6949- Custom objects (dataclasses, ` SimpleNamespace ` , namedtuples) are now
7050 serialized as plain JSON. Decoding such a payload * without* a type hint now
7151 yields a plain ` dict ` (previously a ` SimpleNamespace ` ; a namedtuple now
72- round-trips as a JSON array). To get the original type back, pass the new
73- ` return_type ` / ` data_type ` arguments, annotate the consuming function's
74- parameter or return type, or use the typed client accessors. Payloads produced
75- by older SDK versions still deserialize — including into a ` SimpleNamespace `
76- when no type is supplied — so in-flight orchestrations continue to replay
77- across an upgrade.
52+ round-trips as a JSON array). To get the original type back, supply a type via
53+ one of the mechanisms above. Payloads produced by older SDK versions still
54+ deserialize — including into a ` SimpleNamespace ` when no type is supplied — so
55+ in-flight orchestrations continue to replay across an upgrade.
7856- JSON serialization failures now raise a ` TypeError ` that chains the original
7957 error (` __cause__ ` ) and names the offending type.
58+ - ` EntityContext.get_state() ` / ` DurableEntity.get_state() ` now return a freshly
59+ reconstructed value on every call rather than a reference to a single cached
60+ object. This changes v1.6.0 behavior: mutating the returned value in place no
61+ longer affects persisted state — write it back with ` set_state() ` . State is
62+ also serialized eagerly at ` set_state() ` time, so a non-serializable value
63+ fails inside the operation (which rolls back) instead of after the batch has
64+ run.
8065
8166FIXED
8267
@@ -85,19 +70,31 @@ FIXED
8570 "no state" and written as ` None ` , effectively deleting it; only an actual
8671 ` None ` state now clears the persisted entity state.
8772
88- BREAKING CHANGES (type-level only — no runtime impact for typical users)
73+ DEPRECATED
74+
75+ - ` durabletask.internal.shared.to_json ` and ` durabletask.internal.shared.from_json `
76+ are deprecated and now emit a ` DeprecationWarning ` . Use a
77+ ` durabletask.serialization.DataConverter ` (for example the default
78+ ` JsonDataConverter ` ) instead. The functions continue to work for backwards
79+ compatibility.
80+
81+ BREAKING CHANGES (no runtime impact for typical users)
8982
90- These changes do not alter runtime behavior, but because the package ships
91- ` py.typed ` , consumers running strict type checkers (pyright/mypy) — or
92- subclassing the public abstract types — may need to update their code:
83+ Most of these are type-level only: because the package ships ` py.typed ` ,
84+ consumers running strict type checkers (pyright/mypy) — or subclassing the
85+ public abstract types — may need to update their code. The constructor change
86+ below also affects callers who * directly* construct the named classes, which is
87+ uncommon since they are normally handed to you by the SDK.
9388
9489- ` OrchestrationContext.call_activity ` , ` call_sub_orchestrator ` , ` call_entity ` ,
9590 and ` wait_for_external_event ` gained new keyword-only parameters
9691 (` return_type ` / ` data_type ` ). Subclasses overriding these methods should add
9792 the parameter to match the base signature.
98- - ` client.OrchestrationState ` gained a non-public ` _data_converter ` field
99- (excluded from equality and ` repr ` ). Code constructing ` OrchestrationState `
100- positionally should pass it via the new field or rely on its default.
93+ - ` EntityContext ` and ` EntityMetadata ` (and its ` from_entity_metadata ` /
94+ ` from_entity_response ` factories) now require a ` data_converter ` argument.
95+ These objects are normally constructed by the SDK — you receive an
96+ ` EntityContext ` in an entity function and an ` EntityMetadata ` from the client —
97+ so this only affects code that constructs them directly.
10198
10299## v1.6.0
103100
0 commit comments