From 28678defc33b2566af64c3bc79a3027539aa6b5f Mon Sep 17 00:00:00 2001 From: David Justo Date: Fri, 18 Sep 2020 15:12:22 -0700 Subject: [PATCH 1/3] Revert "Merge w/ Master for release" --- azure/durable_functions/orchestrator.py | 11 +--- tests/orchestrator/test_call_http.py | 30 ++++----- tests/orchestrator/test_fan_out_fan_in.py | 29 +++------ tests/orchestrator/test_retries.py | 19 ++---- .../test_sequential_orchestrator.py | 65 +++---------------- ...test_sequential_orchestrator_with_retry.py | 28 +++----- .../test_sub_orchestrator_with_retry.py | 30 ++++----- 7 files changed, 60 insertions(+), 152 deletions(-) diff --git a/azure/durable_functions/orchestrator.py b/azure/durable_functions/orchestrator.py index 70ee3fd8..9bb06fcf 100644 --- a/azure/durable_functions/orchestrator.py +++ b/azure/durable_functions/orchestrator.py @@ -100,22 +100,13 @@ def handle(self, context: DurableOrchestrationContext): actions=self.durable_context.actions, custom_status=self.durable_context.custom_status) except Exception as e: - exception_str = str(e) orchestration_state = OrchestratorState( is_done=False, output=None, # Should have no output, after generation range actions=self.durable_context.actions, - error=exception_str, + error=str(e), custom_status=self.durable_context.custom_status) - # Create formatted error, using out-of-proc error schema - error_label = "\n\n$OutOfProcData$:" - state_str = orchestration_state.to_json_string() - formatted_error = f"{exception_str}{error_label}{state_str}" - - # Raise exception, re-set stack to original location - raise Exception(formatted_error) from e - # No output if continue_as_new was called if self.durable_context.will_continue_as_new: orchestration_state._output = None diff --git a/tests/orchestrator/test_call_http.py b/tests/orchestrator/test_call_http.py index cb0d95cb..53bcf539 100644 --- a/tests/orchestrator/test_call_http.py +++ b/tests/orchestrator/test_call_http.py @@ -104,25 +104,17 @@ def test_failed_state(): add_failed_http_events( context_builder, 0, failed_reason, failed_details) - try: - result = get_orchestration_state_result( - context_builder, simple_get_generator_function) - # We expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state() - request = get_request() - add_http_action(expected_state, request) - - error_msg = f'{failed_reason} \n {failed_details}' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str + result = get_orchestration_state_result( + context_builder, simple_get_generator_function) + + expected_state = base_expected_state() + request = get_request() + add_http_action(expected_state, request) + expected_state._error = f'{failed_reason} \n {failed_details}' + expected = expected_state.to_json() + + assert_valid_schema(result) + assert_orchestration_state_equals(expected, result) def test_initial_post_state(): diff --git a/tests/orchestrator/test_fan_out_fan_in.py b/tests/orchestrator/test_fan_out_fan_in.py index 5d0c33c5..8a510460 100644 --- a/tests/orchestrator/test_fan_out_fan_in.py +++ b/tests/orchestrator/test_fan_out_fan_in.py @@ -153,22 +153,13 @@ def test_failed_parrot_value(): add_completed_task_set_events(context_builder, 1, 'ParrotValue', activity_count, 2, failed_reason, failed_details) - try: - result = get_orchestration_state_result( - context_builder, generator_function) - # we expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state(error=f'{failed_reason} \n {failed_details}') - add_single_action(expected_state, function_name='GetActivityCount', input_=None) - add_multi_actions(expected_state, function_name='ParrotValue', volume=activity_count) - - error_msg = f'{failed_reason} \n {failed_details}' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str + result = get_orchestration_state_result( + context_builder, generator_function) + + expected_state = base_expected_state(error=f'{failed_reason} \n {failed_details}') + add_single_action(expected_state, function_name='GetActivityCount', input_=None) + add_multi_actions(expected_state, function_name='ParrotValue', volume=activity_count) + expected = expected_state.to_json() + + assert_valid_schema(result) + assert_orchestration_state_equals(expected, result) diff --git a/tests/orchestrator/test_retries.py b/tests/orchestrator/test_retries.py index f1ca4fe7..6e249c50 100644 --- a/tests/orchestrator/test_retries.py +++ b/tests/orchestrator/test_retries.py @@ -255,16 +255,9 @@ def test_retries_can_fail(): """Tests the code path where a retry'ed Task fails""" context = get_context_with_retries(will_fail=True) - try: - result = get_orchestration_state_result( - context, generator_function) - # We expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - error_msg = f"{REASONS} \n {DETAILS}" - - expected_error_str = f"{error_msg}{error_label}" - assert str.startswith(error_str, expected_error_str) \ No newline at end of file + result = get_orchestration_state_result( + context, generator_function) + + expected_error = f"{REASONS} \n {DETAILS}" + assert "error" in result + assert result["error"] == expected_error \ No newline at end of file diff --git a/tests/orchestrator/test_sequential_orchestrator.py b/tests/orchestrator/test_sequential_orchestrator.py index be031265..731c0622 100644 --- a/tests/orchestrator/test_sequential_orchestrator.py +++ b/tests/orchestrator/test_sequential_orchestrator.py @@ -20,18 +20,6 @@ def generator_function(context): return outputs -def generator_function_rasing_ex(context): - outputs = [] - - task1 = yield context.call_activity("Hello", "Tokyo") - task2 = yield context.call_activity("Hello", "Seattle") - task3 = yield context.call_activity("Hello", "London") - - outputs.append(task1) - outputs.append(task2) - outputs.append(task3) - - raise ValueError("Oops!") def generator_function_with_serialization(context): """Ochestrator to test sequential activity calls with a serializable input arguments.""" @@ -111,50 +99,17 @@ def test_failed_tokyo_state(): add_hello_failed_events( context_builder, 0, failed_reason, failed_details) - try: - result = get_orchestration_state_result( - context_builder, generator_function) - # expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state() - add_hello_action(expected_state, 'Tokyo') - error_msg = f'{failed_reason} \n {failed_details}' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str - - -def test_user_code_raises_exception(): - context_builder = ContextBuilder('test_simple_function') - add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"") - add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"") - add_hello_completed_events(context_builder, 2, "\"Hello London!\"") + result = get_orchestration_state_result( + context_builder, generator_function) + + expected_state = base_expected_state() + add_hello_action(expected_state, 'Tokyo') + expected_state._error = f'{failed_reason} \n {failed_details}' + expected = expected_state.to_json() + + assert_valid_schema(result) + assert_orchestration_state_equals(expected, result) - try: - result = get_orchestration_state_result( - context_builder, generator_function_rasing_ex) - # expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state() - add_hello_action(expected_state, 'Tokyo') - add_hello_action(expected_state, 'Seattle') - add_hello_action(expected_state, 'London') - error_msg = 'Oops!' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str def test_tokyo_and_seattle_state(): context_builder = ContextBuilder('test_simple_function') diff --git a/tests/orchestrator/test_sequential_orchestrator_with_retry.py b/tests/orchestrator/test_sequential_orchestrator_with_retry.py index 0356b43b..aafd6ae7 100644 --- a/tests/orchestrator/test_sequential_orchestrator_with_retry.py +++ b/tests/orchestrator/test_sequential_orchestrator_with_retry.py @@ -198,21 +198,13 @@ def test_failed_tokyo_hit_max_attempts(): add_hello_failed_events(context_builder, 4, failed_reason, failed_details) add_retry_timer_events(context_builder, 5) - try: - result = get_orchestration_state_result( - context_builder, generator_function) - # expected an exception - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state() - add_hello_action(expected_state, 'Tokyo') - - error_msg = f'{failed_reason} \n {failed_details}' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str + result = get_orchestration_state_result( + context_builder, generator_function) + + expected_state = base_expected_state() + add_hello_action(expected_state, 'Tokyo') + expected_state._error = f'{failed_reason} \n {failed_details}' + expected = expected_state.to_json() + + assert_valid_schema(result) + assert_orchestration_state_equals(expected, result) diff --git a/tests/orchestrator/test_sub_orchestrator_with_retry.py b/tests/orchestrator/test_sub_orchestrator_with_retry.py index 95b79811..3052ae6c 100644 --- a/tests/orchestrator/test_sub_orchestrator_with_retry.py +++ b/tests/orchestrator/test_sub_orchestrator_with_retry.py @@ -109,21 +109,15 @@ def test_tokyo_and_seattle_and_london_state_all_failed(): add_hello_suborch_failed_events(context_builder, 4, failed_reason, failed_details) add_retry_timer_events(context_builder, 5) - try: - result = get_orchestration_state_result( - context_builder, generator_function) - # Should have error'ed out - assert False - except Exception as e: - error_label = "\n\n$OutOfProcData$:" - error_str = str(e) - - expected_state = base_expected_state() - add_hello_suborch_action(expected_state, 'Tokyo') - - error_msg = f'{failed_reason} \n {failed_details}' - expected_state._error = error_msg - state_str = expected_state.to_json_string() - - expected_error_str = f"{error_msg}{error_label}{state_str}" - assert expected_error_str == error_str \ No newline at end of file + + result = get_orchestration_state_result( + context_builder, generator_function) + + expected_state = base_expected_state() + add_hello_suborch_action(expected_state, 'Tokyo') + expected_state._error = f'{failed_reason} \n {failed_details}' + expected = expected_state.to_json() + expected_state._is_done = True + + #assert_valid_schema(result) + assert_orchestration_state_equals(expected, result) \ No newline at end of file From 8da8dfd152af3fe52b8cac58cd9a1906daf1743b Mon Sep 17 00:00:00 2001 From: David Justo Date: Fri, 18 Sep 2020 15:43:41 -0700 Subject: [PATCH 2/3] Revert "Revert "Merge w/ Master for release"" --- azure/durable_functions/orchestrator.py | 11 +++- tests/orchestrator/test_call_http.py | 30 +++++---- tests/orchestrator/test_fan_out_fan_in.py | 29 ++++++--- tests/orchestrator/test_retries.py | 19 ++++-- .../test_sequential_orchestrator.py | 65 ++++++++++++++++--- ...test_sequential_orchestrator_with_retry.py | 28 +++++--- .../test_sub_orchestrator_with_retry.py | 30 +++++---- 7 files changed, 152 insertions(+), 60 deletions(-) diff --git a/azure/durable_functions/orchestrator.py b/azure/durable_functions/orchestrator.py index 9bb06fcf..70ee3fd8 100644 --- a/azure/durable_functions/orchestrator.py +++ b/azure/durable_functions/orchestrator.py @@ -100,13 +100,22 @@ def handle(self, context: DurableOrchestrationContext): actions=self.durable_context.actions, custom_status=self.durable_context.custom_status) except Exception as e: + exception_str = str(e) orchestration_state = OrchestratorState( is_done=False, output=None, # Should have no output, after generation range actions=self.durable_context.actions, - error=str(e), + error=exception_str, custom_status=self.durable_context.custom_status) + # Create formatted error, using out-of-proc error schema + error_label = "\n\n$OutOfProcData$:" + state_str = orchestration_state.to_json_string() + formatted_error = f"{exception_str}{error_label}{state_str}" + + # Raise exception, re-set stack to original location + raise Exception(formatted_error) from e + # No output if continue_as_new was called if self.durable_context.will_continue_as_new: orchestration_state._output = None diff --git a/tests/orchestrator/test_call_http.py b/tests/orchestrator/test_call_http.py index 53bcf539..cb0d95cb 100644 --- a/tests/orchestrator/test_call_http.py +++ b/tests/orchestrator/test_call_http.py @@ -104,17 +104,25 @@ def test_failed_state(): add_failed_http_events( context_builder, 0, failed_reason, failed_details) - result = get_orchestration_state_result( - context_builder, simple_get_generator_function) - - expected_state = base_expected_state() - request = get_request() - add_http_action(expected_state, request) - expected_state._error = f'{failed_reason} \n {failed_details}' - expected = expected_state.to_json() - - assert_valid_schema(result) - assert_orchestration_state_equals(expected, result) + try: + result = get_orchestration_state_result( + context_builder, simple_get_generator_function) + # We expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state() + request = get_request() + add_http_action(expected_state, request) + + error_msg = f'{failed_reason} \n {failed_details}' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str def test_initial_post_state(): diff --git a/tests/orchestrator/test_fan_out_fan_in.py b/tests/orchestrator/test_fan_out_fan_in.py index 8a510460..5d0c33c5 100644 --- a/tests/orchestrator/test_fan_out_fan_in.py +++ b/tests/orchestrator/test_fan_out_fan_in.py @@ -153,13 +153,22 @@ def test_failed_parrot_value(): add_completed_task_set_events(context_builder, 1, 'ParrotValue', activity_count, 2, failed_reason, failed_details) - result = get_orchestration_state_result( - context_builder, generator_function) - - expected_state = base_expected_state(error=f'{failed_reason} \n {failed_details}') - add_single_action(expected_state, function_name='GetActivityCount', input_=None) - add_multi_actions(expected_state, function_name='ParrotValue', volume=activity_count) - expected = expected_state.to_json() - - assert_valid_schema(result) - assert_orchestration_state_equals(expected, result) + try: + result = get_orchestration_state_result( + context_builder, generator_function) + # we expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state(error=f'{failed_reason} \n {failed_details}') + add_single_action(expected_state, function_name='GetActivityCount', input_=None) + add_multi_actions(expected_state, function_name='ParrotValue', volume=activity_count) + + error_msg = f'{failed_reason} \n {failed_details}' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str diff --git a/tests/orchestrator/test_retries.py b/tests/orchestrator/test_retries.py index 6e249c50..f1ca4fe7 100644 --- a/tests/orchestrator/test_retries.py +++ b/tests/orchestrator/test_retries.py @@ -255,9 +255,16 @@ def test_retries_can_fail(): """Tests the code path where a retry'ed Task fails""" context = get_context_with_retries(will_fail=True) - result = get_orchestration_state_result( - context, generator_function) - - expected_error = f"{REASONS} \n {DETAILS}" - assert "error" in result - assert result["error"] == expected_error \ No newline at end of file + try: + result = get_orchestration_state_result( + context, generator_function) + # We expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + error_msg = f"{REASONS} \n {DETAILS}" + + expected_error_str = f"{error_msg}{error_label}" + assert str.startswith(error_str, expected_error_str) \ No newline at end of file diff --git a/tests/orchestrator/test_sequential_orchestrator.py b/tests/orchestrator/test_sequential_orchestrator.py index 731c0622..be031265 100644 --- a/tests/orchestrator/test_sequential_orchestrator.py +++ b/tests/orchestrator/test_sequential_orchestrator.py @@ -20,6 +20,18 @@ def generator_function(context): return outputs +def generator_function_rasing_ex(context): + outputs = [] + + task1 = yield context.call_activity("Hello", "Tokyo") + task2 = yield context.call_activity("Hello", "Seattle") + task3 = yield context.call_activity("Hello", "London") + + outputs.append(task1) + outputs.append(task2) + outputs.append(task3) + + raise ValueError("Oops!") def generator_function_with_serialization(context): """Ochestrator to test sequential activity calls with a serializable input arguments.""" @@ -99,17 +111,50 @@ def test_failed_tokyo_state(): add_hello_failed_events( context_builder, 0, failed_reason, failed_details) - result = get_orchestration_state_result( - context_builder, generator_function) - - expected_state = base_expected_state() - add_hello_action(expected_state, 'Tokyo') - expected_state._error = f'{failed_reason} \n {failed_details}' - expected = expected_state.to_json() - - assert_valid_schema(result) - assert_orchestration_state_equals(expected, result) + try: + result = get_orchestration_state_result( + context_builder, generator_function) + # expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state() + add_hello_action(expected_state, 'Tokyo') + error_msg = f'{failed_reason} \n {failed_details}' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str + + +def test_user_code_raises_exception(): + context_builder = ContextBuilder('test_simple_function') + add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"") + add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"") + add_hello_completed_events(context_builder, 2, "\"Hello London!\"") + try: + result = get_orchestration_state_result( + context_builder, generator_function_rasing_ex) + # expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state() + add_hello_action(expected_state, 'Tokyo') + add_hello_action(expected_state, 'Seattle') + add_hello_action(expected_state, 'London') + error_msg = 'Oops!' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str def test_tokyo_and_seattle_state(): context_builder = ContextBuilder('test_simple_function') diff --git a/tests/orchestrator/test_sequential_orchestrator_with_retry.py b/tests/orchestrator/test_sequential_orchestrator_with_retry.py index aafd6ae7..0356b43b 100644 --- a/tests/orchestrator/test_sequential_orchestrator_with_retry.py +++ b/tests/orchestrator/test_sequential_orchestrator_with_retry.py @@ -198,13 +198,21 @@ def test_failed_tokyo_hit_max_attempts(): add_hello_failed_events(context_builder, 4, failed_reason, failed_details) add_retry_timer_events(context_builder, 5) - result = get_orchestration_state_result( - context_builder, generator_function) - - expected_state = base_expected_state() - add_hello_action(expected_state, 'Tokyo') - expected_state._error = f'{failed_reason} \n {failed_details}' - expected = expected_state.to_json() - - assert_valid_schema(result) - assert_orchestration_state_equals(expected, result) + try: + result = get_orchestration_state_result( + context_builder, generator_function) + # expected an exception + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state() + add_hello_action(expected_state, 'Tokyo') + + error_msg = f'{failed_reason} \n {failed_details}' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str diff --git a/tests/orchestrator/test_sub_orchestrator_with_retry.py b/tests/orchestrator/test_sub_orchestrator_with_retry.py index 3052ae6c..95b79811 100644 --- a/tests/orchestrator/test_sub_orchestrator_with_retry.py +++ b/tests/orchestrator/test_sub_orchestrator_with_retry.py @@ -109,15 +109,21 @@ def test_tokyo_and_seattle_and_london_state_all_failed(): add_hello_suborch_failed_events(context_builder, 4, failed_reason, failed_details) add_retry_timer_events(context_builder, 5) - - result = get_orchestration_state_result( - context_builder, generator_function) - - expected_state = base_expected_state() - add_hello_suborch_action(expected_state, 'Tokyo') - expected_state._error = f'{failed_reason} \n {failed_details}' - expected = expected_state.to_json() - expected_state._is_done = True - - #assert_valid_schema(result) - assert_orchestration_state_equals(expected, result) \ No newline at end of file + try: + result = get_orchestration_state_result( + context_builder, generator_function) + # Should have error'ed out + assert False + except Exception as e: + error_label = "\n\n$OutOfProcData$:" + error_str = str(e) + + expected_state = base_expected_state() + add_hello_suborch_action(expected_state, 'Tokyo') + + error_msg = f'{failed_reason} \n {failed_details}' + expected_state._error = error_msg + state_str = expected_state.to_json_string() + + expected_error_str = f"{error_msg}{error_label}{state_str}" + assert expected_error_str == error_str \ No newline at end of file From d2cb35b1c3c4ba1681f24f35cf9092d6a08c2aab Mon Sep 17 00:00:00 2001 From: David Justo Date: Thu, 17 Dec 2020 10:44:44 -0800 Subject: [PATCH 3/3] Promoting Dev to Main (#243) --- README.md | 2 +- azure-pipelines.yml | 2 +- azure/durable_functions/entity.py | 8 +++++++- azure/durable_functions/models/DurableEntityContext.py | 3 ++- azure/durable_functions/tasks/task_utilities.py | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 90d313fe..06d7e0c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ |Branch|Status| |---|---| -|master|[![Build Status](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_apis/build/status/Azure%20Functions%20Durable%20Python?branchName=master)](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_build/latest?definitionId=44&branchName=master)| +|main|[![Build Status](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_apis/build/status/Azure%20Functions%20Durable%20Python?branchName=main)](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_build/latest?definitionId=44&branchName=main)| |dev|[![Build Status](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_apis/build/status/Azure%20Functions%20Durable%20Python?branchName=dev)](https://azfunc.visualstudio.com/Azure%20Functions%20Python/_build/latest?definitionId=44&branchName=dev)| # Durable Functions for Python diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4324127d..f14b4593 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,7 @@ trigger: branches: include: - - master + - main - dev tags: include: diff --git a/azure/durable_functions/entity.py b/azure/durable_functions/entity.py index 3c278ff6..c025085d 100644 --- a/azure/durable_functions/entity.py +++ b/azure/durable_functions/entity.py @@ -3,9 +3,13 @@ from datetime import datetime from typing import Callable, Any, List, Dict + class InternalEntityException(Exception): + """Framework-internal Exception class (for internal use only).""" + pass + class Entity: """Durable Entity Class. @@ -51,7 +55,9 @@ def handle(self, context: DurableEntityContext, batch: List[Dict[str, Any]]) -> # populate context operation = operation_data["name"] if operation is None: - raise InternalEntityException("Durable Functions Internal Error: Entity operation was missing a name field") + message = "Durable Functions Internal Error:"\ + "Entity operation was missing a name field" + raise InternalEntityException(message) context._operation = operation context._input = operation_data["input"] self.fn(context) diff --git a/azure/durable_functions/models/DurableEntityContext.py b/azure/durable_functions/models/DurableEntityContext.py index cc1d9814..37cc980c 100644 --- a/azure/durable_functions/models/DurableEntityContext.py +++ b/azure/durable_functions/models/DurableEntityContext.py @@ -179,7 +179,8 @@ def destruct_on_exit(self) -> None: self._exists = False self._state = None -def from_json_util(self, json_str: str) -> Any: + +def from_json_util(json_str: str) -> Any: """Load an arbitrary datatype from its JSON representation. The Out-of-proc SDK has a special JSON encoding strategy diff --git a/azure/durable_functions/tasks/task_utilities.py b/azure/durable_functions/tasks/task_utilities.py index 4487ea25..cfe8bc2c 100644 --- a/azure/durable_functions/tasks/task_utilities.py +++ b/azure/durable_functions/tasks/task_utilities.py @@ -1,7 +1,7 @@ import json from ..models.history import HistoryEventType, HistoryEvent from azure.functions._durable_functions import _deserialize_custom_object -from datetime import datetime +from dateutil import parser from typing import List, Optional, Dict, Any from ..models.actions.Action import Action from ..models.Task import Task