[svn r57546] * channels now also provide makefile(mode) with mode = 'r'
for providing file-like read/readline/close methods. * added and refined crash and finalization tests --HG-- branch : trunk
This commit is contained in:
parent
9b81b15b74
commit
3702ca2c71
|
@ -103,13 +103,16 @@ class Channel(object):
|
||||||
return self._closed
|
return self._closed
|
||||||
|
|
||||||
def makefile(self, mode='w', proxyclose=False):
|
def makefile(self, mode='w', proxyclose=False):
|
||||||
""" return a file-like object. Only supported mode right
|
""" return a file-like object.
|
||||||
now is 'w' for binary writes. If you want to have
|
mode: 'w' for binary writes, 'r' for binary reads
|
||||||
a subsequent file.close() mean to close the channel
|
proxyclose: set to true if you want to have a
|
||||||
as well, then pass proxyclose=True.
|
subsequent file.close() automatically close the channel.
|
||||||
"""
|
"""
|
||||||
assert mode == 'w', "mode %r not availabe" %(mode,)
|
if mode == "w":
|
||||||
return ChannelFile(channel=self, proxyclose=proxyclose)
|
return ChannelFileWrite(channel=self, proxyclose=proxyclose)
|
||||||
|
elif mode == "r":
|
||||||
|
return ChannelFileRead(channel=self, proxyclose=proxyclose)
|
||||||
|
raise ValueError("mode %r not availabe" %(mode,))
|
||||||
|
|
||||||
def close(self, error=None):
|
def close(self, error=None):
|
||||||
""" close down this channel on both sides. """
|
""" close down this channel on both sides. """
|
||||||
|
@ -299,18 +302,11 @@ class ChannelFactory(object):
|
||||||
for id in self._callbacks.keys():
|
for id in self._callbacks.keys():
|
||||||
self._close_callback(id)
|
self._close_callback(id)
|
||||||
|
|
||||||
|
class ChannelFile(object):
|
||||||
class ChannelFile:
|
|
||||||
def __init__(self, channel, proxyclose=True):
|
def __init__(self, channel, proxyclose=True):
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self._proxyclose = proxyclose
|
self._proxyclose = proxyclose
|
||||||
|
|
||||||
def write(self, out):
|
|
||||||
self.channel.send(out)
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self._proxyclose:
|
if self._proxyclose:
|
||||||
self.channel.close()
|
self.channel.close()
|
||||||
|
@ -319,3 +315,38 @@ class ChannelFile:
|
||||||
state = self.channel.isclosed() and 'closed' or 'open'
|
state = self.channel.isclosed() and 'closed' or 'open'
|
||||||
return '<ChannelFile %d %s>' %(self.channel.id, state)
|
return '<ChannelFile %d %s>' %(self.channel.id, state)
|
||||||
|
|
||||||
|
class ChannelFileWrite(ChannelFile):
|
||||||
|
def write(self, out):
|
||||||
|
self.channel.send(out)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ChannelFileRead(ChannelFile):
|
||||||
|
def __init__(self, channel, proxyclose=True):
|
||||||
|
super(ChannelFileRead, self).__init__(channel, proxyclose)
|
||||||
|
self._buffer = ""
|
||||||
|
|
||||||
|
def read(self, n):
|
||||||
|
while len(self._buffer) < n:
|
||||||
|
try:
|
||||||
|
self._buffer += self.channel.receive()
|
||||||
|
except EOFError:
|
||||||
|
self.close()
|
||||||
|
break
|
||||||
|
ret = self._buffer[:n]
|
||||||
|
self._buffer = self._buffer[n:]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def readline(self):
|
||||||
|
i = self._buffer.find("\n")
|
||||||
|
if i != -1:
|
||||||
|
return self.read(i+1)
|
||||||
|
line = self.read(len(self._buffer)+1)
|
||||||
|
while line and line[-1] != "\n":
|
||||||
|
c = self.read(1)
|
||||||
|
if not c:
|
||||||
|
break
|
||||||
|
line += c
|
||||||
|
return line
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,11 @@ class TestPureChannel:
|
||||||
channel = self.fac.new()
|
channel = self.fac.new()
|
||||||
py.test.raises(IOError, channel.waitclose, timeout=0.01)
|
py.test.raises(IOError, channel.waitclose, timeout=0.01)
|
||||||
|
|
||||||
|
def test_channel_makefile_incompatmode(self):
|
||||||
|
channel = self.fac.new()
|
||||||
|
py.test.raises(ValueError, 'channel.makefile("rw")')
|
||||||
|
|
||||||
|
|
||||||
class PopenGatewayTestSetup:
|
class PopenGatewayTestSetup:
|
||||||
def setup_class(cls):
|
def setup_class(cls):
|
||||||
cls.gw = py.execnet.PopenGateway()
|
cls.gw = py.execnet.PopenGateway()
|
||||||
|
@ -291,6 +296,19 @@ class BasicRemoteExecution:
|
||||||
assert isinstance(l[2], channel.__class__)
|
assert isinstance(l[2], channel.__class__)
|
||||||
assert l[3] == 999
|
assert l[3] == 999
|
||||||
|
|
||||||
|
def test_channel_endmarker_callback_error(self):
|
||||||
|
from Queue import Queue
|
||||||
|
q = Queue()
|
||||||
|
channel = self.gw.remote_exec(source='''
|
||||||
|
raise ValueError()
|
||||||
|
''')
|
||||||
|
channel.setcallback(q.put, endmarker=999)
|
||||||
|
val = q.get(TESTTIMEOUT)
|
||||||
|
assert val == 999
|
||||||
|
err = channel._getremoteerror()
|
||||||
|
assert err
|
||||||
|
assert str(err).find("ValueError") != -1
|
||||||
|
|
||||||
def test_remote_redirect_stdout(self):
|
def test_remote_redirect_stdout(self):
|
||||||
out = py.std.StringIO.StringIO()
|
out = py.std.StringIO.StringIO()
|
||||||
handle = self.gw._remote_redirect(stdout=out)
|
handle = self.gw._remote_redirect(stdout=out)
|
||||||
|
@ -315,7 +333,7 @@ class BasicRemoteExecution:
|
||||||
s = subl[0]
|
s = subl[0]
|
||||||
assert s.strip() == str(i)
|
assert s.strip() == str(i)
|
||||||
|
|
||||||
def test_channel_file(self):
|
def test_channel_file_write(self):
|
||||||
channel = self.gw.remote_exec("""
|
channel = self.gw.remote_exec("""
|
||||||
f = channel.makefile()
|
f = channel.makefile()
|
||||||
print >>f, "hello world"
|
print >>f, "hello world"
|
||||||
|
@ -344,6 +362,43 @@ class BasicRemoteExecution:
|
||||||
assert first.strip() == 'hello world'
|
assert first.strip() == 'hello world'
|
||||||
py.test.raises(EOFError, channel.receive)
|
py.test.raises(EOFError, channel.receive)
|
||||||
|
|
||||||
|
def test_channel_file_read(self):
|
||||||
|
channel = self.gw.remote_exec("""
|
||||||
|
f = channel.makefile(mode='r')
|
||||||
|
s = f.read(2)
|
||||||
|
channel.send(s)
|
||||||
|
s = f.read(5)
|
||||||
|
channel.send(s)
|
||||||
|
""")
|
||||||
|
channel.send("xyabcde")
|
||||||
|
s1 = channel.receive()
|
||||||
|
s2 = channel.receive()
|
||||||
|
assert s1 == "xy"
|
||||||
|
assert s2 == "abcde"
|
||||||
|
|
||||||
|
def test_channel_file_read_empty(self):
|
||||||
|
channel = self.gw.remote_exec("pass")
|
||||||
|
f = channel.makefile(mode="r")
|
||||||
|
s = f.read(3)
|
||||||
|
assert s == ""
|
||||||
|
s = f.read(5)
|
||||||
|
assert s == ""
|
||||||
|
|
||||||
|
def test_channel_file_readline_remote(self):
|
||||||
|
channel = self.gw.remote_exec("""
|
||||||
|
channel.send('123\\n45')
|
||||||
|
""")
|
||||||
|
channel.waitclose(TESTTIMEOUT)
|
||||||
|
f = channel.makefile(mode="r")
|
||||||
|
s = f.readline()
|
||||||
|
assert s == "123\n"
|
||||||
|
s = f.readline()
|
||||||
|
assert s == "45"
|
||||||
|
|
||||||
|
def test_channel_makefile_incompatmode(self):
|
||||||
|
channel = self.gw.newchannel()
|
||||||
|
py.test.raises(ValueError, 'channel.makefile("rw")')
|
||||||
|
|
||||||
def test_confusion_from_os_write_stdout(self):
|
def test_confusion_from_os_write_stdout(self):
|
||||||
channel = self.gw.remote_exec("""
|
channel = self.gw.remote_exec("""
|
||||||
import os
|
import os
|
||||||
|
@ -384,6 +439,25 @@ class BasicRemoteExecution:
|
||||||
text = c1.receive()
|
text = c1.receive()
|
||||||
assert text.find("execution disallowed") != -1
|
assert text.find("execution disallowed") != -1
|
||||||
|
|
||||||
|
|
||||||
|
def test_channel_endmarker_remote_killterm():
|
||||||
|
gw = py.execnet.PopenGateway()
|
||||||
|
try:
|
||||||
|
from Queue import Queue
|
||||||
|
q = Queue()
|
||||||
|
channel = gw.remote_exec(source='''
|
||||||
|
import os
|
||||||
|
os.kill(os.getpid(), 15)
|
||||||
|
''')
|
||||||
|
channel.setcallback(q.put, endmarker=999)
|
||||||
|
val = q.get(TESTTIMEOUT)
|
||||||
|
assert val == 999
|
||||||
|
err = channel._getremoteerror()
|
||||||
|
finally:
|
||||||
|
gw.exit()
|
||||||
|
py.test.skip("provide information on causes/signals "
|
||||||
|
"of dying remote gateways")
|
||||||
|
|
||||||
#class TestBlockingIssues:
|
#class TestBlockingIssues:
|
||||||
# def test_join_blocked_execution_gateway(self):
|
# def test_join_blocked_execution_gateway(self):
|
||||||
# gateway = py.execnet.PopenGateway()
|
# gateway = py.execnet.PopenGateway()
|
||||||
|
@ -437,28 +511,43 @@ class TestPopenGateway(PopenGatewayTestSetup, BasicRemoteExecution):
|
||||||
ret = channel.receive()
|
ret = channel.receive()
|
||||||
assert ret == 42
|
assert ret == 42
|
||||||
|
|
||||||
def disabled_test_remote_is_killed_while_sending(self):
|
def test_waitclose_on_remote_killed(self):
|
||||||
|
py.test.skip("fix needed: dying remote process does not cause waitclose() to fail")
|
||||||
|
if not hasattr(py.std.os, 'kill'):
|
||||||
|
py.test.skip("no os.kill")
|
||||||
gw = py.execnet.PopenGateway()
|
gw = py.execnet.PopenGateway()
|
||||||
channel = gw.remote_exec("""
|
channel = gw.remote_exec("""
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
channel.send(os.getppid())
|
|
||||||
channel.send(os.getpid())
|
channel.send(os.getpid())
|
||||||
while 1:
|
while 1:
|
||||||
channel.send('#'*1000)
|
channel.send("#" * 100)
|
||||||
time.sleep(10)
|
|
||||||
""")
|
""")
|
||||||
parent = channel.receive()
|
remotepid = channel.receive()
|
||||||
remote = channel.receive()
|
os.kill(remotepid, 9)
|
||||||
assert parent == os.getpid()
|
py.test.raises(channel.RemoteError, "channel.waitclose(TESTTIMEOUT)")
|
||||||
time.sleep(0.5)
|
py.test.raises(EOFError, channel.send, None)
|
||||||
os.kill(remote, signal.SIGKILL)
|
|
||||||
time.sleep(1)
|
|
||||||
channel.waitclose(TESTTIMEOUT)
|
|
||||||
py.test.raises(EOFError, channel.receive)
|
py.test.raises(EOFError, channel.receive)
|
||||||
#channel.waitclose(TESTTIMEOUT)
|
|
||||||
#channel.send('#')
|
|
||||||
|
|
||||||
|
def test_endmarker_delivery_on_remote_killterm():
|
||||||
|
if not hasattr(py.std.os, 'kill'):
|
||||||
|
py.test.skip("no os.kill()")
|
||||||
|
gw = py.execnet.PopenGateway()
|
||||||
|
try:
|
||||||
|
from Queue import Queue
|
||||||
|
q = Queue()
|
||||||
|
channel = gw.remote_exec(source='''
|
||||||
|
import os
|
||||||
|
os.kill(os.getpid(), 15)
|
||||||
|
''')
|
||||||
|
channel.setcallback(q.put, endmarker=999)
|
||||||
|
val = q.get(TESTTIMEOUT)
|
||||||
|
assert val == 999
|
||||||
|
err = channel._getremoteerror()
|
||||||
|
finally:
|
||||||
|
gw.exit()
|
||||||
|
py.test.skip("provide information on causes/signals "
|
||||||
|
"of dying remote gateways")
|
||||||
|
|
||||||
|
|
||||||
class SocketGatewaySetup:
|
class SocketGatewaySetup:
|
||||||
|
@ -517,3 +606,6 @@ def test_threads_twice():
|
||||||
gw.exit()
|
gw.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def test_nodebug():
|
||||||
|
from py.__.execnet import gateway
|
||||||
|
assert not gateway.debug
|
||||||
|
|
Loading…
Reference in New Issue