feat(python): workflow context propagation quickstart - #1309
Conversation
Ports Cassie Coyle's Go-SDK reference example (dapr/go-sdk#823) to Python. Demonstrates PropagationScope.LINEAGE and PropagationScope.OWN_HISTORY in a fraud-detection payment scenario with a 3-level workflow hierarchy. Requires Dapr 1.18+ (dapr/dapr#9810) and dapr-ext-workflow 1.18+ (dapr/python-sdk#1025). Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
Aligns the Python workflow history propagation quickstart with the canonical Go reference (dapr/go-sdk#823, dapr#1315) so all SDK quickstarts share the same patient intake / e-prescribing scenario. - Swap credit-card/fraud scenario for patient-intake/e-prescribing - Adopt PatientIntake -> PrescribeMedication -> ComplianceAudit hierarchy - Add is_replaying guards around all print() calls inside workflows - Use Cassie's PascalCase activity/workflow names for cross-SDK consistency Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
Aligns the Python workflow history propagation quickstart with Cassie's canonical Go version that merged into release-1.18 (dapr#1315, tutorials/workflow/go/history-propagation). Changes to match the canonical structure: - Move from workflows/python/sdk-context-propagation/order-processor/ to tutorials/workflow/python/history-propagation/, matching the Go sibling at tutorials/workflow/go/history-propagation/ - Split the monolithic app.py into app.py / workflow.py / models.py, mirroring main.go / workflow.go / models.go - Add ForwardLineage flag to PatientRecord and run both scenarios back-to-back (happy path + negative), purging state after each so the app exits on its own - Make DispenseMedication refuse to dispense when no propagated history is received, returning a refused DispenseResult with a Reason field - Match the Go README: title, propagation-scope table, STEP markers, expected output for both scenarios - dapr.yaml: appID=patient-app (was order-processor), resourcesPath=../../resources (was ../../components), appLogDestination/daprdLogDestination=console to match sister Python tutorials and Cassie's Go example - Use correct Python SDK API names: get_last_workflow_by_name / get_last_activity_by_name (the earlier draft used get_workflow_by_name / get_activity_by_name, which were renamed in dapr/python-sdk#1025) Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
|
Updated to align with @cicoyle's now-merged Go quickstart at Location & target branch
File layout
Behavioural alignment with the Go canonical version
Python SDK API
README
No SDK changes were required — the dependency stays at |
| proof — in the propagated history — that the required upstream checks | ||
| (insurance, allergies, drug interactions) actually ran. | ||
|
|
||
| This is the Python sibling of the canonical Go quickstart in |
| [`tutorials/workflow/go/history-propagation`](../../go/history-propagation), | ||
| itself based on [dapr/go-sdk#823](https://github.com/dapr/go-sdk/pull/823). | ||
| Python runtime support landed in | ||
| [dapr/python-sdk#1025](https://github.com/dapr/python-sdk/pull/1025). |
|
|
||
| Requires Dapr `1.18.0+` (workflow history propagation), `go-sdk v1.15.0+`, | ||
| and `durabletask-go v0.12.0+`. | ||
| Requires Dapr `1.18.0+` (workflow history propagation), |
There was a problem hiding this comment.
make this simplier just say 1.18+, no need to specify the sdk
|
|
||
| of scope for this quickstart. | ||
|
|
||
| ## Files |
| ) | ||
|
|
||
| # Step 4: Dispense the medication. | ||
| # Happy path: attach PropagationScope.OWN_HISTORY — the pharmacy sees our |
There was a problem hiding this comment.
In general can you make most of these comments simplier? There is a lot of repetition
…code - README: drop the Go-sibling reference paragraph - README: simplify the requirements line to just "Requires Dapr 1.18+" - README: drop the "exits on its own — no Ctrl+C needed" sentence - README: drop the "## Files" tree at the bottom - workflow.py: trim repetitive docstrings and inline Step comments; the step prints already carry the same information Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
|
Thanks @alicejgibbons — addressed all six in 015fb51: README
workflow.py
|
- Merge 'Key demonstration' into 'Scenarios' so the LINEAGE/OWN_HISTORY behavior is explained once instead of three times. - Cut the unsigned-history warning note from a paragraph to one line.
alicejgibbons
left a comment
There was a problem hiding this comment.
Mostly long comments but a couple other things
Summary
Python sibling of @cicoyle's canonical Go quickstart for workflow history propagation (Dapr 1.18). Same patient intake / e-prescribing scenario, same 3-level hierarchy, same workflow/activity names — so the Python and Go examples can be compared 1:1.
tutorials/workflow/go/history-propagation)dapr-ext-workflow==1.18.0rc0(dapr/python-sdk#1025)release-1.18to land alongside the Go example.Scenario
Patient intake / e-prescribing pipeline. A compliance audit and a pharmacy dispense step refuse to act unless the propagated history proves the required upstream checks (insurance, allergies, drug interactions) actually ran.
ComplianceAuditusesLINEAGEto verify the full ancestor chain.DispenseMedicationusesOWN_HISTORYas a trust boundary — the pharmacy step doesn't get to see the upstream patient-intake chain.Two scenarios (matches the Go demo)
The app schedules both back-to-back and exits on its own — no Ctrl+C needed:
PrescribeMedicationcallsDispenseMedicationwithPropagationScope.OWN_HISTORY; pharmacy verifies and dispenses.PrescribeMedicationcallsDispenseMedicationwithout propagation; pharmacy receives no history and refuses with arefusedDispenseResult.Python API used (from dapr/python-sdk#1025)
PropagationScope.LINEAGE/PropagationScope.OWN_HISTORYpropagation=kwarg onctx.call_child_workflow()andctx.call_activity()ctx.get_propagated_history()→PropagatedHistory | NonePropagatedHistory.get_last_workflow_by_name(name)→WorkflowResultWorkflowResult.get_last_activity_by_name(name)→ActivityResult(with.completed,.output)PropagationNotFoundErrorReplay safety
All
print()calls inside workflows are guarded byif not ctx.is_replaying:so they only fire on the live execution, not on each durable replay.Files
Test plan
pip3 install -r requirements.txtagainstdapr-ext-workflow==1.18.0rc0dapr run -f .against a Dapr 1.18 RC sidecarComplianceAuditreads full ancestor history viaLINEAGE(seesPatientIntake+PrescribeMedication);DispenseMedicationsees onlyPrescribeMedicationevents viaOWN_HISTORYand dispensesDispenseMedicationreceives no propagated history and returnsrefused,PrescribeMedicationreportspharmacy refused to dispenseNonefromget_propagated_history()without crashReferences
tutorials/workflow/go/history-propagation