Skip to content

Commit fc15a55

Browse files
authored
Promote dev to master (#125)
1 parent 7b072f9 commit fc15a55

File tree

74 files changed

+1542
-130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1542
-130
lines changed

azure-pipelines.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ trigger:
88
include:
99
- master
1010
- dev
11-
11+
tags:
12+
include:
13+
- v*
1214

1315
variables:
1416
agentPool: 'ubuntu-latest' # 'Ubuntu-16.04'

azure/durable_functions/grpc/protobuf/DurableRpc.proto

Lines changed: 0 additions & 94 deletions
This file was deleted.

azure/durable_functions/models/DurableOrchestrationClient.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .OrchestrationRuntimeStatus import OrchestrationRuntimeStatus
1414
from ..models import DurableOrchestrationBindings
1515
from .utils.http_utils import get_async_request, post_async_request, delete_async_request
16+
from azure.functions._durable_functions import _serialize_custom_object
1617

1718

1819
class DurableOrchestrationClient:
@@ -166,7 +167,7 @@ async def raise_event(self, instance_id, event_name, event_data=None,
166167

167168
switch_statement = {
168169
202: lambda: None,
169-
410: lambda: None,
170+
410: lambda: f"Instance with ID {instance_id} is gone: either completed or failed",
170171
404: lambda: f"No instance with ID {instance_id} found.",
171172
400: lambda: "Only application/json request content is supported"
172173
}
@@ -432,8 +433,27 @@ def _create_http_response(status_code: int, body: Any) -> func.HttpResponse:
432433
return func.HttpResponse(**response_args)
433434

434435
@staticmethod
435-
def _get_json_input(client_input: object) -> object:
436-
return json.dumps(client_input) if client_input is not None else None
436+
def _get_json_input(client_input: object) -> str:
437+
"""Serialize the orchestrator input.
438+
439+
Parameters
440+
----------
441+
client_input: object
442+
The client's input, which we need to serialize
443+
444+
Returns
445+
-------
446+
str
447+
A string representing the JSON-serialization of `client_input`
448+
449+
Exceptions
450+
----------
451+
TypeError
452+
If the JSON serialization failed, see `serialize_custom_object`
453+
"""
454+
if client_input is not None:
455+
return json.dumps(client_input, default=_serialize_custom_object)
456+
return None
437457

438458
@staticmethod
439459
def _replace_url_origin(request_url, value_url):

azure/durable_functions/models/DurableOrchestrationContext.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from ..models.TokenSource import TokenSource
1111
from ..tasks import call_activity_task, task_all, task_any, call_activity_with_retry_task, \
1212
wait_for_external_event_task, continue_as_new, new_uuid, call_http
13+
from azure.functions._durable_functions import _deserialize_custom_object
1314

1415

1516
class DurableOrchestrationContext:
@@ -27,6 +28,7 @@ def __init__(self,
2728
self._instance_id: str = instanceId
2829
self._is_replaying: bool = isReplaying
2930
self._parent_instance_id: str = parentInstanceId
31+
self._custom_status: Any = None
3032
self._new_uuid_counter: int = 0
3133
self.call_activity = lambda n, i=None: call_activity_task(
3234
state=self.histories,
@@ -78,6 +80,8 @@ def from_json(cls, json_string: str):
7880
DurableOrchestrationContext
7981
New instance of the durable orchestration context class
8082
"""
83+
# We should consider parsing the `Input` field here as well,
84+
# intead of doing so lazily when `get_input` is called.
8185
json_dict = json.loads(json_string)
8286
return cls(**json_dict)
8387

@@ -164,7 +168,8 @@ def call_sub_orchestrator(self,
164168

165169
def get_input(self) -> str:
166170
"""Get the orchestration input."""
167-
return self._input
171+
return None if self._input is None else json.loads(self._input,
172+
object_hook=_deserialize_custom_object)
168173

169174
def new_uuid(self) -> str:
170175
"""Create a new UUID that is safe for replay within an orchestration or operation.
@@ -221,6 +226,23 @@ def task_any(self, activities: List[Task]) -> TaskSet:
221226
"""
222227
raise NotImplementedError("This is a placeholder.")
223228

229+
def set_custom_status(self, status: Any):
230+
"""Set the customized orchestration status for your orchestrator function.
231+
232+
This status is also returned by the orchestration client through the get_status API
233+
234+
Parameters
235+
----------
236+
status : str
237+
Customized status provided by the orchestrator
238+
"""
239+
self._custom_status = status
240+
241+
@property
242+
def custom_status(self):
243+
"""Get customized status of current orchestration."""
244+
return self._custom_status
245+
224246
@property
225247
def histories(self):
226248
"""Get running history of tasks that have been scheduled."""

azure/durable_functions/models/actions/CallActivityAction.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from .Action import Action
44
from .ActionType import ActionType
55
from ..utils.json_utils import add_attrib
6+
from json import dumps
7+
from azure.functions._durable_functions import _serialize_custom_object
68

79

810
class CallActivityAction(Action):
@@ -13,7 +15,8 @@ class CallActivityAction(Action):
1315

1416
def __init__(self, function_name: str, input_=None):
1517
self.function_name: str = function_name
16-
self.input_ = input_
18+
# It appears that `.input_` needs to be JSON-serializable at this point
19+
self.input_ = dumps(input_, default=_serialize_custom_object)
1720

1821
if not self.function_name:
1922
raise ValueError("function_name cannot be empty")

azure/durable_functions/orchestrator.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def __init__(self,
3232
:param activity_func: Generator function to orchestrate.
3333
"""
3434
self.fn: Callable[[DurableOrchestrationContext], Iterator[Any]] = activity_func
35-
self.customStatus: Any = None
3635

3736
def handle(self, context: DurableOrchestrationContext):
3837
"""Handle the orchestration of the user defined generator function.
@@ -46,9 +45,23 @@ def handle(self, context: DurableOrchestrationContext):
4645
the durable extension.
4746
"""
4847
self.durable_context = context
49-
50-
self.generator = self.fn(self.durable_context)
48+
self.generator = None
5149
suspended = False
50+
51+
fn_output = self.fn(self.durable_context)
52+
# If `fn_output` is not an Iterator, then the orchestrator
53+
# function does not make use of its context parameter. If so,
54+
# `fn_output` is the return value instead of a generator
55+
if isinstance(fn_output, Iterator):
56+
self.generator = fn_output
57+
58+
else:
59+
orchestration_state = OrchestratorState(
60+
is_done=True,
61+
output=fn_output,
62+
actions=self.durable_context.actions,
63+
custom_status=self.durable_context.custom_status)
64+
return orchestration_state.to_json_string()
5265
try:
5366
generation_state = self._generate_next(None)
5467

@@ -60,7 +73,7 @@ def handle(self, context: DurableOrchestrationContext):
6073
is_done=False,
6174
output=None,
6275
actions=self.durable_context.actions,
63-
custom_status=self.customStatus)
76+
custom_status=self.durable_context.custom_status)
6477
suspended = True
6578
continue
6679

@@ -79,14 +92,14 @@ def handle(self, context: DurableOrchestrationContext):
7992
is_done=True,
8093
output=sie.value,
8194
actions=self.durable_context.actions,
82-
custom_status=self.customStatus)
95+
custom_status=self.durable_context.custom_status)
8396
except Exception as e:
8497
orchestration_state = OrchestratorState(
8598
is_done=False,
8699
output=None, # Should have no output, after generation range
87100
actions=self.durable_context.actions,
88101
error=str(e),
89-
custom_status=self.customStatus)
102+
custom_status=self.durable_context.custom_status)
90103

91104
return orchestration_state.to_json_string()
92105

azure/durable_functions/tasks/task_utilities.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
from ..models.history import HistoryEventType
3+
from azure.functions._durable_functions import _deserialize_custom_object
34

45

56
def should_suspend(partial_result) -> bool:
@@ -15,12 +16,14 @@ def parse_history_event(directive_result):
1516
if event_type is None:
1617
raise ValueError("EventType is not found in task object")
1718

19+
# We provide the ability to deserialize custom objects, because the output of this
20+
# will be passed directly to the orchestrator as the output of some activity
1821
if event_type == HistoryEventType.EVENT_RAISED:
19-
return json.loads(directive_result.Input)
22+
return json.loads(directive_result.Input, object_hook=_deserialize_custom_object)
2023
if event_type == HistoryEventType.SUB_ORCHESTRATION_INSTANCE_CREATED:
21-
return json.loads(directive_result.Result)
24+
return json.loads(directive_result.Result, object_hook=_deserialize_custom_object)
2225
if event_type == HistoryEventType.TASK_COMPLETED:
23-
return json.loads(directive_result.Result)
26+
return json.loads(directive_result.Result, object_hook=_deserialize_custom_object)
2427
return None
2528

2629

noxfile.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ def tests(session):
55
# same as pip install -r -requirements.txt
66
session.install("-r", "requirements.txt")
77
session.install("pytest")
8-
session.run("pytest","-v","tests")
8+
session.run("pytest", "-v", "tests")
9+
910

1011
@nox.session(python="3.7")
1112
def lint(session):
1213
session.install("flake8")
13-
session.run("flake8","./azure/**py")
14-
14+
session.install("flake8-docstrings")
15+
session.run("flake8", "./azure/")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.git*
2+
.vscode
3+
local.settings.json
4+
test
5+
.venv

0 commit comments

Comments
 (0)