From 3e82f95d9ef8b00ce29b948284c7930f59bc4faa Mon Sep 17 00:00:00 2001 From: David Justo Date: Fri, 28 May 2021 16:22:04 -0700 Subject: [PATCH 1/2] typechecking codebase, missing only aiohttp --- azure/__init__.py | 2 +- .../models/DurableOrchestrationClient.py | 10 +++++++++- azure/durable_functions/models/history/HistoryEvent.py | 7 +++++++ .../stubs/azure.functions._durable_functions.pyi | 4 ++++ azure/durable_functions/stubs/azure.functions.pyi | 8 ++++++++ azure/durable_functions/tasks/call_entity.py | 2 +- azure/durable_functions/tasks/task_all.py | 2 +- azure/durable_functions/tasks/task_utilities.py | 8 ++++++-- 8 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 azure/durable_functions/stubs/azure.functions._durable_functions.pyi create mode 100644 azure/durable_functions/stubs/azure.functions.pyi diff --git a/azure/__init__.py b/azure/__init__.py index d378fc67..7fcf6557 100644 --- a/azure/__init__.py +++ b/azure/__init__.py @@ -1,3 +1,3 @@ """Base module for the Python Durable functions.""" from pkgutil import extend_path -__path__ = extend_path(__path__, __name__) +__path__ = extend_path(__path__, __name__) # type: ignore diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 76554c5f..df3033eb 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -396,6 +396,7 @@ async def terminate(self, instance_id: str, reason: str) -> None: if error_message: raise Exception(error_message) + async def wait_for_completion_or_create_check_status_response( self, request, instance_id: str, timeout_in_milliseconds: int = 10000, retry_interval_in_milliseconds: int = 1000) -> func.HttpResponse: @@ -440,6 +441,8 @@ async def wait_for_completion_or_create_check_status_response( lambda: self._create_http_response(200, status.to_json()), OrchestrationRuntimeStatus.Failed: lambda: self._create_http_response(500, status.to_json()), + None: + None } result = switch_statement.get(status.runtime_status) @@ -456,6 +459,7 @@ async def wait_for_completion_or_create_check_status_response( await sleep(sleep_time) else: return self.create_check_status_response(request, instance_id) + return self.create_check_status_response(request, instance_id) async def signal_entity(self, entityId: EntityId, operation_name: str, operation_input: Optional[Any] = None, @@ -640,6 +644,7 @@ async def rewind(self, response = await self._post_async_request(request_url, None) status: int = response[0] + ex_msg: str = "" if status == 200 or status == 202: return elif status == 404: @@ -648,6 +653,9 @@ async def rewind(self, elif status == 410: ex_msg = "The rewind operation is only supported on failed orchestration instances." raise Exception(ex_msg) - else: + elif isinstance(response[1], str): ex_msg = response[1] raise Exception(ex_msg) + else: + ex_msg = "Received unexpected payload from the durable-extension: " + str(response) + raise Exception(ex_msg) diff --git a/azure/durable_functions/models/history/HistoryEvent.py b/azure/durable_functions/models/history/HistoryEvent.py index 84023944..fa6585b1 100644 --- a/azure/durable_functions/models/history/HistoryEvent.py +++ b/azure/durable_functions/models/history/HistoryEvent.py @@ -14,6 +14,13 @@ def __init__(self, EventType: HistoryEventType, EventId: int, IsPlayed: bool, Ti self._is_played: bool = IsPlayed self._timestamp: datetime.datetime = dt_parse(Timestamp) self._is_processed: bool = False + + self.Name = None + self.InstanceId = None + self.TaskScheduledId = None + self.Reason = None + self.Details = None + self.Input = None if kwargs is not None: for key, value in kwargs.items(): self.__setattr__(key, value) diff --git a/azure/durable_functions/stubs/azure.functions._durable_functions.pyi b/azure/durable_functions/stubs/azure.functions._durable_functions.pyi new file mode 100644 index 00000000..117363ff --- /dev/null +++ b/azure/durable_functions/stubs/azure.functions._durable_functions.pyi @@ -0,0 +1,4 @@ +from typing import Any + +def _deserialize_custom_object(obj: dict) -> object: ... +def _serialize_custom_object(obj) -> dict[str, Any]: ... \ No newline at end of file diff --git a/azure/durable_functions/stubs/azure.functions.pyi b/azure/durable_functions/stubs/azure.functions.pyi new file mode 100644 index 00000000..d687e1da --- /dev/null +++ b/azure/durable_functions/stubs/azure.functions.pyi @@ -0,0 +1,8 @@ +class OrchestrationContext: ... +class HttpRequest: + @property + def url(self) -> str: ... +class HttpResponse: + def __init__(body=None, *, status_code=None, headers=None, mimetype=None, charset=None): ... + +# look into azure func implementations to complete stubs \ No newline at end of file diff --git a/azure/durable_functions/tasks/call_entity.py b/azure/durable_functions/tasks/call_entity.py index 467e5b63..5d920405 100644 --- a/azure/durable_functions/tasks/call_entity.py +++ b/azure/durable_functions/tasks/call_entity.py @@ -51,7 +51,7 @@ def call_entity_task( event_raised = None if event_sent: event_input = None - if hasattr(event_sent, "Input"): + if hasattr(event_sent, "Input") and event_sent.Input is not None: event_input = RequestMessage.from_json(event_sent.Input) hist_type = HistoryEventType.EVENT_RAISED extra_constraints = { diff --git a/azure/durable_functions/tasks/task_all.py b/azure/durable_functions/tasks/task_all.py index 0758ae61..9d19a917 100644 --- a/azure/durable_functions/tasks/task_all.py +++ b/azure/durable_functions/tasks/task_all.py @@ -59,7 +59,7 @@ def task_all(tasks: List[Task]): # Incomplete TaskSets do not have results or end-time if not is_completed: - results = None + results = [] end_time = None # Construct TaskSet diff --git a/azure/durable_functions/tasks/task_utilities.py b/azure/durable_functions/tasks/task_utilities.py index cfe8bc2c..e063f7b7 100644 --- a/azure/durable_functions/tasks/task_utilities.py +++ b/azure/durable_functions/tasks/task_utilities.py @@ -304,14 +304,18 @@ def gen_err_message(counter: int, mid_message: str, found: str, expected: str) - # TODO: The HistoryEvent does not necessarily have a name or an instance_id # We should create sub-classes of these types like JS does, to ensure their # precense. + + if event.Name is None: + raise ValueError("History Event for suborchestration found with no {Name} field") + event_name: str = event.Name err_message: str = "" if not(event.Name == name): mid_message = "a function name of {} instead of the provided function name of {}." - err_message = gen_err_message(counter, mid_message, event.Name, name) + err_message = gen_err_message(counter, mid_message, event_name, name) raise ValueError(err_message) if instance_id and not(event.InstanceId == instance_id): mid_message = "an instance id of {} instead of the provided instance id of {}." - err_message = gen_err_message(counter, mid_message, event.Name, name) + err_message = gen_err_message(counter, mid_message, event_name, name) raise ValueError(err_message) return event From b896efc955d90ede9f2b749017db096defe804c7 Mon Sep 17 00:00:00 2001 From: David Justo Date: Tue, 1 Jun 2021 14:39:22 -0700 Subject: [PATCH 2/2] Lint, and fix noxfile --- azure/__init__.py | 2 +- .../models/DurableOrchestrationClient.py | 1 - .../stubs/azure.functions._durable_functions.pyi | 4 ---- azure/durable_functions/stubs/azure.functions.pyi | 8 -------- azure/durable_functions/tasks/task_utilities.py | 2 +- noxfile.py | 1 + 6 files changed, 3 insertions(+), 15 deletions(-) delete mode 100644 azure/durable_functions/stubs/azure.functions._durable_functions.pyi delete mode 100644 azure/durable_functions/stubs/azure.functions.pyi diff --git a/azure/__init__.py b/azure/__init__.py index 7fcf6557..db7d5de9 100644 --- a/azure/__init__.py +++ b/azure/__init__.py @@ -1,3 +1,3 @@ """Base module for the Python Durable functions.""" from pkgutil import extend_path -__path__ = extend_path(__path__, __name__) # type: ignore +__path__ = extend_path(__path__, __name__) # type: ignore diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index df3033eb..3f4bb163 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -396,7 +396,6 @@ async def terminate(self, instance_id: str, reason: str) -> None: if error_message: raise Exception(error_message) - async def wait_for_completion_or_create_check_status_response( self, request, instance_id: str, timeout_in_milliseconds: int = 10000, retry_interval_in_milliseconds: int = 1000) -> func.HttpResponse: diff --git a/azure/durable_functions/stubs/azure.functions._durable_functions.pyi b/azure/durable_functions/stubs/azure.functions._durable_functions.pyi deleted file mode 100644 index 117363ff..00000000 --- a/azure/durable_functions/stubs/azure.functions._durable_functions.pyi +++ /dev/null @@ -1,4 +0,0 @@ -from typing import Any - -def _deserialize_custom_object(obj: dict) -> object: ... -def _serialize_custom_object(obj) -> dict[str, Any]: ... \ No newline at end of file diff --git a/azure/durable_functions/stubs/azure.functions.pyi b/azure/durable_functions/stubs/azure.functions.pyi deleted file mode 100644 index d687e1da..00000000 --- a/azure/durable_functions/stubs/azure.functions.pyi +++ /dev/null @@ -1,8 +0,0 @@ -class OrchestrationContext: ... -class HttpRequest: - @property - def url(self) -> str: ... -class HttpResponse: - def __init__(body=None, *, status_code=None, headers=None, mimetype=None, charset=None): ... - -# look into azure func implementations to complete stubs \ No newline at end of file diff --git a/azure/durable_functions/tasks/task_utilities.py b/azure/durable_functions/tasks/task_utilities.py index e063f7b7..b0dd0251 100644 --- a/azure/durable_functions/tasks/task_utilities.py +++ b/azure/durable_functions/tasks/task_utilities.py @@ -304,7 +304,7 @@ def gen_err_message(counter: int, mid_message: str, found: str, expected: str) - # TODO: The HistoryEvent does not necessarily have a name or an instance_id # We should create sub-classes of these types like JS does, to ensure their # precense. - + if event.Name is None: raise ValueError("History Event for suborchestration found with no {Name} field") event_name: str = event.Name diff --git a/noxfile.py b/noxfile.py index 25632006..cae7ac72 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,5 +16,6 @@ def lint(session): @nox.session(python=["3.7", "3.8"]) def typecheck(session): + session.install("-r", "requirements.txt") session.install("mypy") session.run("mypy", "./azure/") \ No newline at end of file