mirror of
https://github.com/wassname/ray.git
synced 2026-07-09 23:43:21 +08:00
Enable actor methods to be decorated on the caller side also and get postprocessors. (#4732)
* Allow decorating ray actor methods. * Add test. * Add get postprocessors. * Improve documentation. * Make it work for remote functions. * Temporary fix.
This commit is contained in:
committed by
Philipp Moritz
parent
897b35ce36
commit
d81e71e297
+64
-11
@@ -109,10 +109,36 @@ def method(*args, **kwargs):
|
||||
# Create objects to wrap method invocations. This is done so that we can
|
||||
# invoke methods with actor.method.remote() instead of actor.method().
|
||||
class ActorMethod(object):
|
||||
def __init__(self, actor, method_name, num_return_vals):
|
||||
"""A class used to invoke an actor method.
|
||||
|
||||
Note: This class is instantiated only while the actor method is being
|
||||
invoked (so that it doesn't keep a reference to the actor handle and
|
||||
prevent it from going out of scope).
|
||||
|
||||
Attributes:
|
||||
_actor: A handle to the actor.
|
||||
_method_name: The name of the actor method.
|
||||
_num_return_vals: The default number of return values that the method
|
||||
invocation should return.
|
||||
_decorator: An optional decorator that should be applied to the actor
|
||||
method invocation (as opposed to the actor method execution) before
|
||||
invoking the method. The decorator must return a function that
|
||||
takes in two arguments ("args" and "kwargs"). In most cases, it
|
||||
should call the function that was passed into the decorator and
|
||||
return the resulting ObjectIDs. For an example, see
|
||||
"test_decorated_method" in "python/ray/tests/test_actor.py".
|
||||
"""
|
||||
|
||||
def __init__(self, actor, method_name, num_return_vals, decorator=None):
|
||||
self._actor = actor
|
||||
self._method_name = method_name
|
||||
self._num_return_vals = num_return_vals
|
||||
# This is a decorator that is used to wrap the function invocation (as
|
||||
# opposed to the function execution). The decorator must return a
|
||||
# function that takes in two arguments ("args" and "kwargs"). In most
|
||||
# cases, it should call the function that was passed into the decorator
|
||||
# and return the resulting ObjectIDs.
|
||||
self._decorator = decorator
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise Exception("Actor methods cannot be called directly. Instead "
|
||||
@@ -131,11 +157,18 @@ class ActorMethod(object):
|
||||
if num_return_vals is None:
|
||||
num_return_vals = self._num_return_vals
|
||||
|
||||
return self._actor._actor_method_call(
|
||||
self._method_name,
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
num_return_vals=num_return_vals)
|
||||
def invocation(args, kwargs):
|
||||
return self._actor._actor_method_call(
|
||||
self._method_name,
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
num_return_vals=num_return_vals)
|
||||
|
||||
# Apply the decorator if there is one.
|
||||
if self._decorator is not None:
|
||||
invocation = self._decorator(invocation)
|
||||
|
||||
return invocation(args, kwargs)
|
||||
|
||||
|
||||
class ActorClass(object):
|
||||
@@ -157,6 +190,10 @@ class ActorClass(object):
|
||||
_exported: True if the actor class has been exported and false
|
||||
otherwise.
|
||||
_actor_methods: The actor methods.
|
||||
_method_decorators: Optional decorators that should be applied to the
|
||||
method invocation function before invoking the actor methods. These
|
||||
can be set by attaching the attribute
|
||||
"__ray_invocation_decorator__" to the actor method.
|
||||
_method_signatures: The signatures of the methods.
|
||||
_actor_method_names: The names of the actor methods.
|
||||
_actor_method_num_return_vals: The default number of return values for
|
||||
@@ -196,6 +233,7 @@ class ActorClass(object):
|
||||
# Extract the signatures of each of the methods. This will be used
|
||||
# to catch some errors if the methods are called with inappropriate
|
||||
# arguments.
|
||||
self._method_decorators = {}
|
||||
self._method_signatures = {}
|
||||
self._actor_method_num_return_vals = {}
|
||||
for method_name, method in self._actor_methods:
|
||||
@@ -214,6 +252,10 @@ class ActorClass(object):
|
||||
self._actor_method_num_return_vals[method_name] = (
|
||||
ray_constants.DEFAULT_ACTOR_METHOD_NUM_RETURN_VALS)
|
||||
|
||||
if hasattr(method, "__ray_invocation_decorator__"):
|
||||
self._method_decorators[method_name] = (
|
||||
method.__ray_invocation_decorator__)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise Exception("Actors methods cannot be instantiated directly. "
|
||||
"Instead of running '{}()', try '{}.remote()'.".format(
|
||||
@@ -337,9 +379,9 @@ class ActorClass(object):
|
||||
|
||||
actor_handle = ActorHandle(
|
||||
actor_id, self._modified_class.__module__, self._class_name,
|
||||
actor_cursor, self._actor_method_names, self._method_signatures,
|
||||
self._actor_method_num_return_vals, actor_cursor, actor_method_cpu,
|
||||
worker.task_driver_id)
|
||||
actor_cursor, self._actor_method_names, self._method_decorators,
|
||||
self._method_signatures, self._actor_method_num_return_vals,
|
||||
actor_cursor, actor_method_cpu, worker.task_driver_id)
|
||||
# We increment the actor counter by 1 to account for the actor creation
|
||||
# task.
|
||||
actor_handle._ray_actor_counter += 1
|
||||
@@ -381,6 +423,10 @@ class ActorHandle(object):
|
||||
_ray_actor_counter: The number of actor method invocations that we've
|
||||
called so far.
|
||||
_ray_actor_method_names: The names of the actor methods.
|
||||
_ray_method_decorators: Optional decorators for the function
|
||||
invocation. This can be used to change the behavior on the
|
||||
invocation side, whereas a regular decorator can be used to change
|
||||
the behavior on the execution side.
|
||||
_ray_method_signatures: The signatures of the actor methods.
|
||||
_ray_method_num_return_vals: The default number of return values for
|
||||
each method.
|
||||
@@ -407,6 +453,7 @@ class ActorHandle(object):
|
||||
class_name,
|
||||
actor_cursor,
|
||||
actor_method_names,
|
||||
method_decorators,
|
||||
method_signatures,
|
||||
method_num_return_vals,
|
||||
actor_creation_dummy_object_id,
|
||||
@@ -428,6 +475,7 @@ class ActorHandle(object):
|
||||
self._ray_actor_cursor = actor_cursor
|
||||
self._ray_actor_counter = 0
|
||||
self._ray_actor_method_names = actor_method_names
|
||||
self._ray_method_decorators = method_decorators
|
||||
self._ray_method_signatures = method_signatures
|
||||
self._ray_method_num_return_vals = method_num_return_vals
|
||||
self._ray_class_name = class_name
|
||||
@@ -530,8 +578,11 @@ class ActorHandle(object):
|
||||
# this was causing cyclic references which were prevent
|
||||
# object deallocation from behaving in a predictable
|
||||
# manner.
|
||||
return ActorMethod(self, attr,
|
||||
self._ray_method_num_return_vals[attr])
|
||||
return ActorMethod(
|
||||
self,
|
||||
attr,
|
||||
self._ray_method_num_return_vals[attr],
|
||||
decorator=self._ray_method_decorators.get(attr))
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@@ -600,6 +651,7 @@ class ActorHandle(object):
|
||||
"class_name": self._ray_class_name,
|
||||
"actor_cursor": self._ray_actor_cursor,
|
||||
"actor_method_names": self._ray_actor_method_names,
|
||||
"method_decorators": self._ray_method_decorators,
|
||||
"method_signatures": self._ray_method_signatures,
|
||||
"method_num_return_vals": self._ray_method_num_return_vals,
|
||||
# Actors in local mode don't have dummy objects.
|
||||
@@ -662,6 +714,7 @@ class ActorHandle(object):
|
||||
state["class_name"],
|
||||
state["actor_cursor"],
|
||||
state["actor_method_names"],
|
||||
state["method_decorators"],
|
||||
state["method_signatures"],
|
||||
state["method_num_return_vals"],
|
||||
state["actor_creation_dummy_object_id"],
|
||||
|
||||
Reference in New Issue
Block a user