-
Notifications
You must be signed in to change notification settings - Fork 103
[Feature Request] Suggest Providing activities extraction utilities to get them from a class and a module #758
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
Comments
Sorry, just noticed this feature issue. Having discussion if viability on PR at #759 (comment). Also circulating with team. |
Hi @spacether, thanks for the input here. Before getting into implementation, can you expand on the problem that you want to see solved and what you see as the requirements for possible solutions? I.e. expand on this
E.g. what is it that you don't like currently, and, without speculating about specific implementations, what sorts of behaviors / semantics regarding activities would you like users to be able to express when starting a worker? It might help if you sketch some Worker launch code featuring imaginary APIs / function calls that would give you the semantics that you want. |
So right now it is entirely up to a develop to manually build an explicit list of activities in python. Our activities are already segregated by the kind fo work that they do into a python modules and activity classes. So then when we need to use them in a worker, one has to to list every activity decorated funcction in that module and in one or two classes when we already know that the activities I need come from these 3 sources: 1 module, and two classes. Adding these utilities lets me vend activities from those sources easily. I would like:
or
or
One could make activities accept a list of (callables or a module or a class that has activities methods in it), where all of the activities from the class or module would be loaded into the worker. |
I recently came to the same conclusion as @spacether and made a similar function for collecting activities as part of a utility library for enforcing best practices at test time to avoid runtime failures. My developers use a collection function in a worker.py file so that they don't forget to add an activity method to the worker every time they write a new one. I also have a function that is intended to run in a test, you just import your project's temporal directory module and it gets every @activity.defn method in the module and submodules and so that we can validate everything automatically: Repo is very much a work in progress still, not much documentation for the function-based validation as opposed to inheriting from a special validator class but the collection methods are here: https://github.com/noxasaxon/temporal_utils_python/blob/main/src/temporal_utils/collectors.py |
I've resorted more than once to wrapping temporal's activity and workflow decorators in my own to make activity and workflow collection easy. Typically speaking, I have two needs:
Manual import is messy because
Personally I'd be just as happy with a method that provides for easy collection, e.g. from temporalio import worker
import my_activities
from other_activities import ActivityClass
w = worker.Worker(
...,
activities=worker.collect_activities(
"path/to/activity/folder",
my_activities,
ActivityClass
), ...
) together with some simple filters. @activity.defn(skip=True)
async def my_activity():
... This would facilitate e.g. test cases, dev/prod feature flags, and the like. More flexible and comprehensive would be the ability to configure tags (this could easily be used in lieu of @activity.defn(tags=["dev", "e2e"])
async def prototyped_task():
... together with filtering directives in the collect_activities function: activities = worker.collect_activities(module, tags=["prod"])
activities = worker.collect_activities(module, exclude_tags=["test"]) Finally, some kind of name pattern filter might be nice (though once again this could generally be handled by tags): activities = worker.collect_activities(module, regex=r".*_v[45]|database_.*") My activities are usually bare functions so I don't have a real opinion on the best approach for activities that are owned by a class or class instance. |
As a side note, one important issue to address is being able to deal effectively with mocks for tests. @flows.activity
async def real_activity():
...
@flows.activity(worker="other_worker")
async def other_activity():
...
async def real_activity_mock():
...
async def other_activity_mock():
...
@flows.testing.case
@flows.testing.provide(activities={"real_activity": other_activity_mock})
@pytest.mark.asyncio
async def test_with_mock_other_activity(flow_runner):
await flow_runner.execute_workflow(my_workflow, ...)
# actually mainly used to mock activities handled by remote workers,
# but can be used with other workers in the same process
@flows.testing.case
@flows.testing.provide("other_worker", activities={"other_activity": other_activity_mock})
@pytest.mark.asyncio
async def test_with_mock_other_activity(flow_runner):
await flow_runner.execute_workflow(multi_worker_workflow, ...) With collection by a function as above configuring concise declarative tests might be more challenging. worker.collect_activities(module, overrides={"my_activity": my_activity_mock}) |
Uh oh!
There was an error while loading. Please reload this page.
Is your feature request related to a problem? Please describe.
Creating activities and decorating them is easy using activy.def in python
Making sure that they are all included in worker launch is more difficult.
Describe the solution you'd like
It would be helpful if a utility was provided that allowed
Below is a sample implementation for extraction from class and class instances, assuming async method implementations that uses ast and inspection.
It looks like one could find which methods are decorate by checking if
fn.__temporal_activity_definition
but that is a private variable name and is not exposed in the temporalio activity.py module. Code that uses __temporal_activity_definition would be simpler and not invokingast.parse(inspect.getsource(cls))
is preferrable.And some Tests:
Additional context
In code that I am working on activities are mainly defined in one module when they are fns and in class methods.
The text was updated successfully, but these errors were encountered: