Using Python 3.11 and RPyC 5.3.0, I see a very puzzling interaction when talking to an RPyc server from a client that uses a pyqtgraph PlotWindow widget. (Pyqtgraph is a well-known framework for making plots in python programs that implement a Qt-based GUI).
I am testing under Linux, with PyQt5 but the issue is also present when using PySide6.
The server code is as plain and simple as can be:
#! /usr/bin/env python3
import logging
import rpyc
# Configure the logging infrastructure.
logging.basicConfig(level=logging.DEBUG)
class MyService(rpyc.Service):
def __init__(self):
super().__init__()
self.exposed_value = 42
server = rpyc.utils.server.ThreadedServer(MyService(), port=30000)
server.start()
The client side is somewhat more involved. I open a connection to the server and interact with it. Next, I run a simple GUI application that stops when the user closes the window. Finally, I close the connection:
#! /usr/bin/env python3
import sys
import logging
import time
import rpyc
from PyQt5.QtWidgets import QApplication, QWidget
import pyqtgraph as pg
# Configure the logging infrastructure.
logging.basicConfig(level=logging.DEBUG)
# Connect to the server's MyService instance.
connection = rpyc.connect("localhost", 30000)
# Obtain a value.
# If we don't do this, the server-side issue disappears.
value = connection.root.value
print(value)
# Make a trivial Qt application and run it.
app = QApplication(sys.argv)
main_window = pg.PlotWidget() # If we use this, the server will emit an error upon closing the window.
#main_window = QWidget() # If we use this instead, the server will be happy.
main_window.show()
exitcode = app.exec()
# As soon as we return from the application, the server-side generates a strange debug message.
# Wait a bit (to establish when the server-side error is emitted), then close the connection.
time.sleep(10)
connection.close()
The issue is that as soon as I close the GUI application, the server-side emits a debugging message that strongly suggests something has gone wrong. This is before the connection is closed:
[email protected]:~/rpc_pyqtgraph_issue$ ./server.py
INFO:MY/30000:server started on [0.0.0.0]:30000
INFO:MY/30000:accepted ('127.0.0.1', 43422) with fd 4
INFO:MY/30000:welcome ('127.0.0.1', 43422)
DEBUG:MY/30000:Exception caught
Traceback (most recent call last):
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 356, in _dispatch_request
res = self._HANDLERS[handler](self, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 853, in _handle_getattr
return self._access_attr(obj, name, (), "_rpyc_getattr", "allow_getattr", getattr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 780, in _access_attr
name = self._check_attr(obj, name, param)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 770, in _check_attr
raise AttributeError(f"cannot access {name!r}")
AttributeError: cannot access '__class__'
DEBUG:MY/30000:Exception caught
Traceback (most recent call last):
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 356, in _dispatch_request
res = self._HANDLERS[handler](self, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 853, in _handle_getattr
return self._access_attr(obj, name, (), "_rpyc_getattr", "allow_getattr", getattr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 780, in _access_attr
name = self._check_attr(obj, name, param)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/sidney/local_python/root/lib/python3.11/site-packages/rpyc/core/protocol.py", line 770, in _check_attr
raise AttributeError(f"cannot access {name!r}")
AttributeError: cannot access '__class__'
INFO:MY/30000:goodbye ('127.0.0.1', 43422)
^C
WARNING:MY/30000:keyboard interrupt!
INFO:MY/30000:server has terminated
INFO:MY/30000:listener closed
There's a couple of ways I can make this debugging noise disappear.
(1) If I don't interact from the client with the server other than opening/closing it, the issue disappears.
(2) If I use a plain QWidget rather than a pyqtgraph PlotWidget, the issue disappears.
My conclusion is that there is some kind of a funky interaction between pyqtgraph and rpyc; but I am not well-enough versed in both their codebases to investigate any further -- so I am hoping someone here can reproduce the issue, and see what's going on.