dapr / python-sdk

Dapr SDK for Python
Apache License 2.0
230 stars 128 forks source link

[Workflow] Zero argument values sent to activities are deserialized as None #574

Closed cgillum closed 1 year ago

cgillum commented 1 year ago

Expected Behavior

When calling an activity from a workflow and passing an input of 0, it should remain a 0 in the activity code.

Actual Behavior

The 0 value seems to be translated into None at runtime. This is likely a serialization bug either in the workflow SDK or in the underlying Durable Task Python SDK.

Steps to Reproduce the Problem

Observe the output from the following code to see the problem:

import dapr.ext.workflow as wf

def example_workflow(ctx: wf.DaprWorkflowContext, wf_input: int):
    if not ctx.is_replaying:
        print(f'Workflow input: {wf_input}.')
    yield ctx.call_activity(example_activity, input=wf_input)

def example_activity(ctx, activity_input: int):
    print(f'Activity input: {activity_input}.')

if __name__ == '__main__':
    workflowRuntime = wf.WorkflowRuntime("localhost", "50001")
    workflowRuntime.register_workflow(example_workflow)
    workflowRuntime.register_activity(example_activity)
    workflowRuntime.start()

    wf_client = wf.DaprWorkflowClient()
    instance_id = wf_client.schedule_new_workflow(workflow=example_workflow, input=0)
    print(f'Workflow started. Instance ID: {instance_id}')
    state = wf_client.wait_for_workflow_completion(instance_id)

    workflowRuntime.shutdown()

The output will look something like this:

Workflow input: 0.
Activity input: None.

Note that the activity input value should be 0, not None. It seems that workflow inputs are handled correctly.

Release Note

RELEASE NOTE: FIX [Workflow] Handling of zero values in activity inputs

cgillum commented 1 year ago

/cc @DeepanshuA

DeepanshuA commented 1 year ago

/assign

RyanLettieri commented 1 year ago

I'd like to expand on this bug with the following information: If a workflow definition does not include an input, i.e.

def example_workflow(ctx: wf.DaprWorkflowContext):
    if not ctx.is_replaying:
        print('Workflow')
    yield ctx.call_activity(example_activity, input="something")
versus
def example_workflow(ctx: wf.DaprWorkflowContext, wf_input: int):
    if not ctx.is_replaying:
        print(f'Workflow input: {wf_input}.')
    yield ctx.call_activity(example_activity, input=wf_input)

It can lead to a FAILED workflow

DeepanshuA commented 1 year ago

This has got 2 different issues enlisted:

  1. Activity Input treats 0 as None: For this, a small fix in DTF-python has been raised https://github.com/microsoft/durabletask-python/pull/13
  2. A workflow or Activity doesn't work without input: For this https://github.com/dapr/python-sdk/pull/589 has been raised.
cgillum commented 1 year ago

@berndverst this issue is not fixed. It still requires the change here: https://github.com/microsoft/durabletask-python/pull/13.

Can this issue be reactivated until the Python SDK has been updated with the corresponding Durable Task SDK fix?