Defining an actor with pydantic

Hi,
I’m using pydantic and ray in mu project, and pydantic returns an error on actors. Here is a snippet of code to reproduce the issue:

@ray.remote
class TestWorker(BaseModel):
    class Config:
        arbitrary_types_allowed = True
        extra = 'allow'
        allow_mutation = True

tw = TestWorker.remote()

the error is:

Traceback (most recent call last):
  File ".....py", line 18, in <module>
    class TestWorker(BaseModel):
  File "/.../lib/python3.7/site-packages/ray/_private/client_mode_hook.py", line 47, in wrapper
    return func(*args, **kwargs)
  File ".../lib/python3.7/site-packages/ray/worker.py", line 1876, in remote
    return make_decorator(worker=worker)(args[0])
  File ".../lib/python3.7/site-packages/ray/worker.py", line 1761, in decorator max_task_retries)
  File ".../lib/python3.7/site-packages/ray/actor.py", line 1005, in make_actor accelerator_type)
  File ".../lib/python3.7/site-packages/ray/actor.py", line 380, in 
    _ray_from_modified_class resources, accelerator_type)
  File "pydantic/main.py", line 415, in pydantic.main.BaseModel.__setattr__

AttributeError: fields_set

Is there a way to fix this issue?
I’m using ray 1.2.0

cc @simon-mo Can you answer the question?

Hi @delioda79, Pydantic is supposed to just contains plain data and perform validation on it. It doesn’t fit the actor model where you are suppose to host a forever running instance of an object. Can you explain a bit about your use case?

I would recommend something like:

class Model(BaseModel):
    class Config:
        arbitrary_types_allowed = True
        extra = 'allow'
        allow_mutation = True

@ray.remote
class TestWorker():
    def __init__(model: Model):
        self.model = model
     def do_something(self, msg):
        pass

tw = TestWorker.remote(Model(...))

Regardless, can you try pydantic 1.6.1?

Thanks, I’ve tried pydantic 1.6.1 and the error is the same. On another side, I can surely do what you suggested in you code snippet, but to be honest I don’t see how data validation does not fit with an ever running function. Pydantic is used for data validation, to enforce types at runtime, not just to create data classes. The fact that a function belonging to a class runs forever or once it is irrelevant to the fact that I want ensure types and existence for the attributes I want to set to the actor. Besides, pydantic offers also a validation system for methods through validate_arguments which again, is independent on how the function will run. What I mean is that these are two different problems, not related at all.

Now, about my specific case, my actor has some dependencies, which are integers, strings, and other classes’ instances. Now what I want is to validate the types of these attributes, which I inject when I instantiate the actor, and to validate that these have been set. This is just something mimicking what strictly typing does, and that’s why pydantic is useful.

Furthermore, but this might be complicated, I would like to inject other actors, and I would like to make sure these actors are the remote version of a specific class. Again, all I want is to ensure types and raise an error if the attributes are not set or set to a wrong type.

As an example:

class MyAverageClass(BaseModel):
   name: str
   count = 0
   ......................
  def my_average_function(self):
      print("I'm average")


@ray.remote
class MyAwesomeActor(BaseModel):
   name: str
   count = 0
  ......................
  def my_awesome_function(self):
      print("I'm awesome")


@ray.remote
class StoicActor(BaseModel);
   name: str
   awa: MyAwesomeActor
   avo: MyAverageClass

  def my_stoic_function(self):
    self.awa.my_awesome_function.remote()
   print("No you are not")
   self.avo.my_average_function()
   print("you are better than that")

sa = StoicActor.remote(name="Epicurus", avo=MyAverageClass(name="John Doe"), MyAwesomeActor(name="Whoever"))

I know I don;t need to ensure types…but then we would not need pydantic at all. I just come from strict typing and for large projects it helps, so I would like to use pydantic wherever I can.