-
Notifications
You must be signed in to change notification settings - Fork 251
Description
Hello - first off I'd like to thank the developer(s) for creating such a great project, really impressive!
I've recently started using rpyc and I noticed something odd with how errors are handled on the server-side in the case of a remote object being used as a context manager on the client side.
Specifically, I have an application where I'd like to use a with
statement in my client-side code on a remote object that has __enter__
and __exit__
functions defined. I am able to see the correct error type and message on the client side, but on the server side I get another exception:
caught an exception: type=<class 'TypeError'>, val=exceptions must derive from BaseException
I have included a minimal example below that shows the issue.
I have traced this with my debugger to rpyc.core.protocol, line 891, the function _handle_ctxexit
. When my example server-side code raises a built-in error (ValueError in this case) I can see that the right exception is making its way into this function, but somehow the raise exc
statement is failing, which is caught in the except Exception
statement and a TypeError is returned:
def _handle_ctxexit(self, obj, exc): # request handler
if exc:
try:
raise exc
except Exception:
exc, typ, tb = sys.exc_info()
else:
typ = tb = None
return self._handle_getattr(obj, "__exit__")(exc, typ, tb)
My expectation is that both the client and the server should see the same type of exception, and that we should be able to filter and handle exceptions in the __exit__
statement on the remote object.
To reproduce this error, start the server, then run the client.
Server Output:
% python minimal_server.py
in __enter__
in __exit__
caught an exception: type=<class 'TypeError'>, val=exceptions must derive from BaseException
Client output
% python minimal_client.py
Traceback (most recent call last):
File "/path/to/rpyc-example/minimal_client.py", line 6, in <module>
conn.root.raise_remote()
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/netref.py", line 239, in __call__
return syncreq(_self, consts.HANDLE_CALL, args, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/netref.py", line 63, in syncreq
return conn.sync_request(handler, proxy, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/protocol.py", line 744, in sync_request
return _async_res.value
^^^^^^^^^^^^^^^^
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/async_.py", line 111, in value
raise self._obj
_get_exception_class.<locals>.Derived: Hello, World!
========= Remote Traceback (1) =========
Traceback (most recent call last):
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/protocol.py", line 369, in _dispatch_request
res = self._HANDLERS[handler](self, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/rpyc-example/venv/lib/python3.11/site-packages/rpyc/core/protocol.py", line 863, in _handle_call
return obj(*args, **dict(kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/rpyc-example/minimal_server.py", line 14, in raise_remote
raise ValueError("Hello, World!")
ValueError: Hello, World!
Environment
- rpyc version: 6.0.1
- python version: 3.11.3
- operating system: Darwin (Mac Ventura 13.6.9)
Minimal example
Server:
from rpyc import Service, ThreadedServer
class MyService(Service):
""" example service showing TypeError in __exit__ """
def on_connect(self, conn):
pass
def on_disconnect(self, conn):
pass
def raise_remote(self):
raise ValueError("Hello, World!")
def __enter__(self):
print("in __enter__")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("in __exit__")
if exc_type:
print(f"caught an exception: type={exc_type}, val={exc_val}")
return
if __name__ == "__main__":
t = ThreadedServer(
MyService(),
port=18861,
protocol_config={
'allow_public_attrs': True
}
)
t.start()
Client:
import rpyc
conn = rpyc.connect("localhost", port=18861)
with conn.root:
conn.root.raise_remote()