Fault Error in Proxy

We can create a simple library as follows:

- lib
    -__init__.py
                from .my_class import myClass
    - my_class.py
                class myClass:
                      def foo(self):
                          return 0

So we can call it in vscode:

import lib
print(lib.myClass().foo())
>>> 0

Now, we want to use Proxy in GH to call it:

from compas.rpc import Proxy
with Proxy('lib', path = r"PATH\TO\LIB") as lib:
    print(lib.myClass().foo())

The error is:

Runtime error (Exception): Fault

1 Like

Did you specify the “foo” method as @classmethod or @staticmethod to run it without initialization of the class? I guess proxy use function calls.

Also I am wondering if it is a good idea to have proxy methods in a class in general. In most cases you will use proxy because you want to use a specific C based python library for large datasets. Conceptually it is better to split into a separate python function as a helper function. And run it as explained in the documentation.

On another note, did you try to use Rhino8 Beta where you can directly run C python libraries without proxy? It will be more straight forward: Rhino 8 Feature: ScriptEditor (CPython, CSharp) - Serengeti (Rhino 8 BETA) - McNeel Forum Just be sure that your libraries are compiled to the version of Python3 that the RhinoCode use.

It feels that in a long term, Rhino will have a proper Python3 integration.

I tried it as both an @classmethod and @staticmethod and both returned Fault. I also tried using proxy to create an instance of the class and then call the function “foo”, but still no luck!

As for proxy methods in the class or not, it is still something we are discussing but we have not decided on yet!

And I did not think about testing it in the Rhino8 Beta… that is an interesting thought and definetly good food for thought! :slight_smile:

I am not fully aware what you are doing since you do not post the full code.
From common sense, I would start from something that works.

But maybe this example helps. This is how I use functions of proxy in Rhino:


from compas.rpc import Proxy  # https://compas.dev/compas/latest/api/compas.rpc.html

proxy = Proxy("compas_wood.joinery")  # import module

# call function methods
# get current directory
foldername = proxy.get_cwd() + "/frontend/src/wood/dataset/"

# read polylines from an xml files
id = feasible_ids[max(min(24, _id), 0)]
filename_of_dataset = proxy.get_filenames_of_datasets()[id]

# read xml file
output_polylines = proxy.read_xml_polylines(foldername, filename_of_dataset)

# get timber connections
result = proxy.get_connection_zones(
            output_polylines,
            vectors,  # output_vectors,
            joint_types,  # output_joints_types,
            three_valence,  # 
            output_three_valence_element_indices_and_instruction,
            _adjacency,  # output_adjacency,
            joint_parameters,
            search,
            scale,
            output_type,
            False,
            extension,
        )

And the module itself are just methods:

My apologies, it is a series of methods to interact with an online web server. It was previously just a collection of methods that worked fine when called with proxy, but the organization of the overall code structure would benefit from it being organized in a class structure if possible. But I think we have a solution for this! Thank you for all of your help! :slight_smile:

hi,

you can actually already do something in the direction of what you are looking for. you just have to define a custom service. it is not mega convenient but i can make it a bit simpler to use with a few small updates…

# myservices/apiservice.py

from compas.rpc import Dispatcher
from compas.rpc import Server


class SpecialService(Dispatcher):
    def __init__(self):
        super(SpecialService, self).__init__()

    # define your API functions here

    def special(self):
        return "special"


def start_service(port, autoreload, **kwargs):
    print("Starting default RPC service on port {0}...".format(port))
    server = Server(("0.0.0.0", port))
    service = SpecialService()
    server.register_function(server.ping)
    server.register_function(server.remote_shutdown)
    server.register_instance(service)
    print("Listening{}...".format(" with autoreload of modules enabled" if autoreload else ""))
    print("Press CTRL+C to abort")
    server.serve_forever()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("--port", "-p", action="store", default=1753, type=int, help="RPC port number")
    parser.add_argument(
        "--autoreload",
        dest="autoreload",
        action="store_true",
        help="Autoreload modules",
    )
    parser.add_argument(
        "--no-autoreload",
        dest="autoreload",
        action="store_false",
        help="Do not autoreload modules",
    )
    parser.set_defaults(autoreload=False, func=start_service)

    args = parser.parse_args()

    if hasattr(args, "func"):
        args.func(**vars(args))
    else:
        parser.print_help()

# client.py

from compa.rpc import Proxy

proxy = Proxy(service='myservices.apiservice')

print(proxy.special())

Note that this currently only works if the service is on your pythonpath. And it doesn’t work if autoreload is turned on, but these things can be fixed quite easily. We can also remove the unnecessary clutter so you only have to define a custom service without all of the other stuff…

Hello Tom, thank you for the information! We decided to go a different route to try to avoid using Proxy and optimize the speed of the program so no need to worry. I did not think about trying to define a custom service, but that would be very interesting and helpful for testing things in development. Thank you for the information! :slight_smile: