Run the following test program:
source ="""
class AsyncStream(object):
def __init__(self, stream):
import rpyc
from rpyc.core.consts import HANDLE_CALL
assert isinstance(stream, rpyc.BaseNetref)
self._stream = stream
self.origwrite = stream.write
self.conn = object.__getattribute__(self.origwrite,
"____conn__")
self.oid = object.__getattribute__(self.origwrite,
"____oid__")
self.HANDLE_CALL = HANDLE_CALL
def __getattr__(self, attr):
return getattr(self._stream, attr)
def readline(self, size=None):
try:
return self._stream.readline(size)
except KeyboardInterrupt:
raise KeyboardInterrupt, "Operation Cancelled"
def write(self, message):
conn = self.conn()
conn.async_request(self.HANDLE_CALL, self.oid, (message,), {})
while len(conn._async_callbacks) > 100 :
conn.serve()
def asyncIO():
import sys
sys.stdin = AsyncStream(sys.stdin)
sys.stdout = AsyncStream(sys.stdout)
sys.stderr = AsyncStream(sys.stderr)
asyncIO()
"""
import rpyc
from rpyc.utils.classic import redirected_stdio
c = rpyc.classic.connect("localhost")
redirect = redirected_stdio(c)
try:
c.execute(source)
c.execute("for i in range(10): print i")
finally:
redirect.restore()
c.close()
The output is
9
8
7
...
instead of
0
1
2
3
...
This used to work as expected in rpyc 2.6. I have tried hard to
understand where the reversal occurs at no avail. Any ideas?
Many thanks.
The problem is in the following statement
conn.async_request(self.HANDLE_CALL, self.oid, (message,), {})
The empty dict that is passed as an argument is causing conn.serve to
be called recursively (!) resulting in the results appearing in
reverse order.
One solultion is to pass an empty tuple instead of an empty dict,
which is a trick used in class _Async
conn.async_request(self.HANDLE_CALL, self.oid, (message,), ())
This works because conn._handle_call is defined as:
def _handle_call(self, oid, args, kwargs):
return self._local_objects[oid](*args, **dict(kwargs))
***I would suggest*** that you change the definition of _handle_call
to
def _handle_call(self, oid, args, kwargs={}):
return self._local_objects[oid](*args, **dict(kwargs))
This does not break anything and allows avoiding passing the keyword
dictionary when it is empty.
conn.async_request(self.HANDLE_CALL, self.oid, (message,))
or more efficiently
conn._async_request(self.HANDLE_CALL, (self.oid, (message,)))