The problem is now solved, with the help of colleagues.
The problem is that the program using gevent requires a monkey-patched copy of socket, whereas py4j requires an unpatched copy, i.e. two copies present at the same time.
In python 2 this could be achieved by reloading socket before setting up the py4j gateway, and repatching it afterwards. In Python 3 there is only ever one copy of a loaded module (much better). The same result could then be obtained (see
https://stackoverflow.com/questions/11170949/how-to-make-a-copy-of-a-python-module-at-runtime) by something like
import socket as original_socket
del sys.modules["socket"]
del sys.modules["_socket"]
import socket
gevent.monkey.patch_all(thread=False)
followed by
py4j.java_gateway.socket = original_socket
py4j.clientserver.socket = original_socket
Which inserts the unpatched version of socket into py4j.
This is still somewhat hacky, since it depends on the (unguaranteed) internals of py4j, but it works and is an improvement over the Python 2 version.