-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial code for decorators and optional naming of workflows and activities #651
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #651 +/- ##
==========================================
+ Coverage 86.16% 86.43% +0.27%
==========================================
Files 75 75
Lines 3737 3893 +156
==========================================
+ Hits 3220 3365 +145
- Misses 517 528 +11 ☔ View full report in Codecov by Sentry. |
assert client_activity._registered_name == self.mock_client_activity.__name__ | ||
|
||
def test_both_decorator_and_register(self): | ||
client_wf = (self.runtime_options.workflow(name="test_wf"))(self.mock_client_wf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When called with the @
operator, the function object is replaced in place with the wrapped function only.
Mimicking that here for checking _registered_name
attribute.
I have specifically not changed the |
Hi, I like where this is going but I have some suggestions that would make it easier for my use case. I think it would be helpful to offer a decorator function that sets the function All of our code explicitly passes an already instantiated WorkflowRuntime instance from the top-most level to registration functions in lower-level modules. This way we only instantiate a WorkflowRuntime once, at the top level, and not on every module import. Being able to set Then |
…vities Signed-off-by: Mukundan Sundararajan <[email protected]>
bd4041a
to
2688f3c
Compare
I believe that the name a function registers with for a workflow should be provided only during the register call or be the name of the function itself. I do not think we should add a separate decorator for adding a Instead is there any specific scenario where the optional name cannot be set with the |
Please consider my current project, which integrates multiple systems together. Each integration is in its own module, and each integration has its own set of workflows and activities. Most integrations have the same basic activities:
etc. A separate higher-level orchestration module combines workflows and activities together as needed. In that higher-level module, it's much cleaner to write code like this: from wms_integration.dapr.activities import generic_order as generic_order_act
...
result = yield ctx.call_activity(generic_order_act.parse_file, input) But because there are multiple So I end up with code like this with ugly and long names: result = yield ctx.call_activity(
generic_order_act.generic_order_parse_order_file_act,
input,
) That is why I do not want to use the name of the function itself. Instantiating a WorkflowRuntime in every module, just so that the registered name can be set by a decorator, is problematic. Imports should not have side effects, but creating a WorkflowRuntime creates a GrpcEndpoint and a TaskHubGrpcWorker. Every module would have to know the correct host and port to use (which is not always going to be localhost nor the default port).
Certainly you can and should support optionally setting the name during the register call. But it would be better to ALSO support setting the name with a decorator (a decorator that does not require an existing WorkflowRuntime due to the side effect issues pointed out above). The reason to use a decorator to set the registered name is so that all concerns related to the function are kept together in its declaration, in one spot: (the python function name, call signature, docstring and dapr registered name). In my use case, I have modules with only activities. Suppose those modules are 1000 lines long. At the bottom of the module is a function that is called by higher-level code, which passes in WorkflowRuntime so that this module's activities can be registered: def register_activities(wf_runtime: WorkflowRuntime):
"""register activities"""
wf_runtime.register_activity(generic_order_parse_order_file_act)
wf_runtime.register_activity(generic_order_import_order_act)
wf_runtime.register_activity(generate_email_request_from_summary_act) But my function declaration is 950 lines higher up in the code, far away from where the dapr registered_name is set. That's not great. Those concerns would be far removed from each other. Maybe this isn't too important for activities, but for workflows it is important because other developers will want to look at the workflow function definition and quickly determine a) the python name they can use to call the workflow directly, or b) the dapr name to use to instantiate a workflow by registered name. Having those two values far apart just increases developer workload. In summary, I did not ask you remove any functionality that you have already implemented. I merely proposed that you add support to register_x() so that it doesn't overwrite an already existing Thanks for reading this far.. |
Signed-off-by: Mukundan Sundararajan <[email protected]>
2c11db0
to
29bcbb7
Compare
Signed-off-by: Mukundan Sundararajan <[email protected]>
a3752ff
to
c95e8b7
Compare
@bkcsfi I have modified to add an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small comments.
|
||
Args: | ||
name (Optional[str], optional): Name to identify the workflow function as in | ||
the workflow runtime. Defaults to None. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the workflow runtime. Defaults to None. | |
the workflow runtime. Defaults to the name of the function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, it does default to None
, but is then overridden by the function name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it depends on whether this description is intended to describe the function signature or describe the what the user can expect - i.e., the semantic behavior.
Signed-off-by: Mukundan Sundararajan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good with these changes. 👍🏽
@mukundansundar Hi thank you for revising the decorator and register functions. The changes meet all my needs. Do you have an existing issue or discussion for I am using some ugly approaches to try to keep activity and workflow 'input deseralization' and 'output serialization' tied to activity and workflow function declarations, rather than forcing the caller to do that work. I would appreciate seeing what you're planning there, as well as maybe getting durable task to Thanks, |
I will be creating an issue following a comment/discussion from you in discord. |
Signed-off-by: Mukundan Sundararajan <[email protected]>
…vities (dapr#651) * initial code for decorators and optional naming of workflows and activities Signed-off-by: Mukundan Sundararajan <[email protected]> * add alternater_name decorator Signed-off-by: Mukundan Sundararajan <[email protected]> * fix linter Signed-off-by: Mukundan Sundararajan <[email protected]> * address review comments. Signed-off-by: Mukundan Sundararajan <[email protected]> * address review comments. Signed-off-by: Mukundan Sundararajan <[email protected]> --------- Signed-off-by: Mukundan Sundararajan <[email protected]>
Description
_alternate_name
for eachworkflow
,activity
function/method and when a new name a given, thealternate_name
which is the user given name takes precedence over the__name__
of the function/method.Following through with the discussion in #575, I have added partial validation to the
chaining
andfan out fan in
examples alone as the other two require user input and take a longer time to complete also.@berndverst and @cgillum For now I have left the
register_workflow
and theregister_activity
methods as is without any changes, so users can use either a decorator patter or useregister_workflow
andregister_activity
for now.So if we continue to support both, there has to be the case that any parameter or functionality added as part of the decorator will in essence be added as part of the
register_*
methods, that is the code flow today also.Issue reference
We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.
part of #635
closes #623
Checklist
Please make sure you've completed the relevant tasks for this PR, out of the following list: