Errors using `@ray.remote` decorator on a Python subclass from a Cython wrapper

Is there a recommended way to create a Ray remote class (actor pattern) from a Python class that is subclassed from a Cython wrapper?

When I use Cython to wrap a C++ class, then create a Python subclass from that – even though the code works fine, Ray throws exceptions when I add the @ray.remote decorator to the Python subclass. From the exceptions, it appears that the constructor does not have the correct arguments. Is there a recommended way to work around this?

Here’s an example:

the libffurf.hpp C++ header file:

namespace ffurf {
  class Foo {
  public:
    int x;

    Foo ();
    Foo (int x);
    ~Foo ();
  };
}

the libffurf.cpp C++ source file:

#include "libffurf.hpp"

using namespace ffurf;

// default constructor
Foo::Foo () {}

// overloaded constructor
Foo::Foo (int x) {
  this->x = x;
}

// destructor
Foo::~Foo () {}

the ffurf_part.pyx Cython extension file:

cdef extern from "libffurf.hpp" namespace "ffurf":
    cdef cppclass Foo:
        Foo (int) except +
        int x

cdef class CyFoo:
    cdef Foo *cpp_obj

    def __cinit__ (self, int x):
        self.cpp_obj = new Foo(x)

the partact.py Python source file used for testing:

import ray
import ffurf_part

@ray.remote
class Foo (ffurf_part.CyFoo):
    def see_foo ():
        print("hello")

the error traceback:

Traceback (most recent call last):
  File "partact.py", line 8, in <module>
    class Foo (ffurf_part.CyFoo):
  File "/home/paco/anaconda3/lib/python3.8/site-packages/ray/worker.py", line 1990, in remote
    return make_decorator(worker=worker)(args[0])
  File "/home/paco/anaconda3/lib/python3.8/site-packages/ray/worker.py", line 1868, in decorator
    return ray.actor.make_actor(function_or_class, num_cpus, num_gpus,
  File "/home/paco/anaconda3/lib/python3.8/site-packages/ray/actor.py", line 1069, in make_actor
    return ActorClass._ray_from_modified_class(
  File "/home/paco/anaconda3/lib/python3.8/site-packages/ray/actor.py", line 383, in _ray_from_modified_class
    self = DerivedActorClass.__new__(DerivedActorClass)
  File "ffurf_part.pyx", line 26, in ffurf_part.CyFoo.__cinit__
    def __cinit__ (self, int x):
TypeError: __cinit__() takes exactly 1 positional argument (0 given)

When I remove the @ray.remote decorator, then instantiate a Foo object and call its methods, this example works fine.

The Ray documentation describes about use of remote functions with Cython, although not about using remote classes.

There’s clearly a conflict if a class constructor requires any positional arguments. A workaround is to refactor those into an initialize() method.

Cython documentation is a bit sparse too, especially for Python use of classes defined in C++ when arguments other than simple integers are needed.

Here’s a working example of how to resolve these issues and use Ray and Cython together, with the explicit caveat that Ray does not officially support this: