[svn r37766] unifying non-FD and FD capturing some more
(could be more, but at least the APIs start to feel the same) --HG-- branch : trunk
This commit is contained in:
parent
9ddca27edc
commit
17754c6fd9
|
@ -4,6 +4,8 @@ import py
|
||||||
try: from cStringIO import StringIO
|
try: from cStringIO import StringIO
|
||||||
except ImportError: from StringIO import StringIO
|
except ImportError: from StringIO import StringIO
|
||||||
|
|
||||||
|
emptyfile = StringIO()
|
||||||
|
|
||||||
class Capture(object):
|
class Capture(object):
|
||||||
def call(cls, func, *args, **kwargs):
|
def call(cls, func, *args, **kwargs):
|
||||||
""" return a (res, out, err) tuple where
|
""" return a (res, out, err) tuple where
|
||||||
|
@ -24,13 +26,17 @@ class StdCaptureFD(Capture):
|
||||||
""" capture Stdout and Stderr both on filedescriptor
|
""" capture Stdout and Stderr both on filedescriptor
|
||||||
and sys.stdout/stderr level.
|
and sys.stdout/stderr level.
|
||||||
"""
|
"""
|
||||||
def __init__(self, out=True, err=True, patchsys=True):
|
def __init__(self, out=True, err=True, mixed=False, patchsys=True):
|
||||||
if out:
|
if out:
|
||||||
self.out = py.io.FDCapture(1)
|
self.out = py.io.FDCapture(1)
|
||||||
if patchsys:
|
if patchsys:
|
||||||
self.out.setasfile('stdout')
|
self.out.setasfile('stdout')
|
||||||
if err:
|
if err:
|
||||||
self.err = py.io.FDCapture(2)
|
if mixed and out:
|
||||||
|
tmpfile = self.out.tmpfile
|
||||||
|
else:
|
||||||
|
tmpfile = None
|
||||||
|
self.err = py.io.FDCapture(2, tmpfile=tmpfile)
|
||||||
if patchsys:
|
if patchsys:
|
||||||
self.err.setasfile('stderr')
|
self.err.setasfile('stderr')
|
||||||
|
|
||||||
|
@ -40,14 +46,12 @@ class StdCaptureFD(Capture):
|
||||||
returns a tuple of file objects (out, err) for the captured
|
returns a tuple of file objects (out, err) for the captured
|
||||||
data
|
data
|
||||||
"""
|
"""
|
||||||
out = err = ""
|
outfile = errfile = emptyfile
|
||||||
if hasattr(self, 'out'):
|
if hasattr(self, 'out'):
|
||||||
outfile = self.out.done()
|
outfile = self.out.done()
|
||||||
out = outfile.read()
|
|
||||||
if hasattr(self, 'err'):
|
if hasattr(self, 'err'):
|
||||||
errfile = self.err.done()
|
errfile = self.err.done()
|
||||||
err = errfile.read()
|
return outfile.read(), errfile.read()
|
||||||
return out, err
|
|
||||||
|
|
||||||
class StdCapture(Capture):
|
class StdCapture(Capture):
|
||||||
""" capture sys.stdout/sys.stderr (but not system level fd 1 and 2).
|
""" capture sys.stdout/sys.stderr (but not system level fd 1 and 2).
|
||||||
|
@ -55,13 +59,21 @@ class StdCapture(Capture):
|
||||||
This class allows to capture writes to sys.stdout|stderr "in-memory"
|
This class allows to capture writes to sys.stdout|stderr "in-memory"
|
||||||
and will raise errors on tries to read from sys.stdin.
|
and will raise errors on tries to read from sys.stdin.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, out=True, err=True, mixed=False):
|
||||||
|
self._out = out
|
||||||
|
self._err = err
|
||||||
|
if out:
|
||||||
|
self.oldout = sys.stdout
|
||||||
|
sys.stdout = self.newout = StringIO()
|
||||||
|
if err:
|
||||||
|
self.olderr = sys.stderr
|
||||||
|
if out and mixed:
|
||||||
|
newerr = self.newout
|
||||||
|
else:
|
||||||
|
newerr = StringIO()
|
||||||
|
sys.stderr = self.newerr = newerr
|
||||||
self.oldin = sys.stdin
|
self.oldin = sys.stdin
|
||||||
self.oldout = sys.stdout
|
|
||||||
self.olderr = sys.stderr
|
|
||||||
sys.stdin = self.newin = DontReadFromInput()
|
sys.stdin = self.newin = DontReadFromInput()
|
||||||
sys.stdout = self.newout = StringIO()
|
|
||||||
sys.stderr = self.newerr = StringIO()
|
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
""" return captured output and restore sys.stdout/err."""
|
""" return captured output and restore sys.stdout/err."""
|
||||||
|
@ -70,13 +82,25 @@ class StdCapture(Capture):
|
||||||
|
|
||||||
def done(self):
|
def done(self):
|
||||||
o,e = sys.stdout, sys.stderr
|
o,e = sys.stdout, sys.stderr
|
||||||
sys.stdin, sys.stdout, sys.stderr = (
|
outfile = errfile = emptyfile
|
||||||
self.oldin, self.oldout, self.olderr)
|
if self._out:
|
||||||
del self.oldin, self.oldout, self.olderr
|
try:
|
||||||
o, e = self.newout, self.newerr
|
sys.stdout = self.oldout
|
||||||
o.seek(0)
|
except AttributeError:
|
||||||
e.seek(0)
|
raise IOError("stdout capturing already reset")
|
||||||
return o,e
|
del self.oldout
|
||||||
|
outfile = self.newout
|
||||||
|
outfile.seek(0)
|
||||||
|
if self._err:
|
||||||
|
try:
|
||||||
|
sys.stderr = self.olderr
|
||||||
|
except AttributeError:
|
||||||
|
raise IOError("stderr capturing already reset")
|
||||||
|
del self.olderr
|
||||||
|
errfile = self.newerr
|
||||||
|
errfile.seek(0)
|
||||||
|
sys.stdin = self.oldin
|
||||||
|
return outfile, errfile
|
||||||
|
|
||||||
class DontReadFromInput:
|
class DontReadFromInput:
|
||||||
"""Temporary stub class. Ideally when stdin is accessed, the
|
"""Temporary stub class. Ideally when stdin is accessed, the
|
||||||
|
|
|
@ -57,9 +57,9 @@ class TestFDCapture:
|
||||||
tmpfp.close()
|
tmpfp.close()
|
||||||
f = cap.done()
|
f = cap.done()
|
||||||
|
|
||||||
class TestCapturing:
|
class TestStdCapture:
|
||||||
def getcapture(self):
|
def getcapture(self, **kw):
|
||||||
return py.io.StdCaptureFD()
|
return py.io.StdCapture(**kw)
|
||||||
|
|
||||||
def test_capturing_simple(self):
|
def test_capturing_simple(self):
|
||||||
cap = self.getcapture()
|
cap = self.getcapture()
|
||||||
|
@ -69,6 +69,15 @@ class TestCapturing:
|
||||||
assert out == "hello world\n"
|
assert out == "hello world\n"
|
||||||
assert err == "hello error\n"
|
assert err == "hello error\n"
|
||||||
|
|
||||||
|
def test_capturing_mixed(self):
|
||||||
|
cap = self.getcapture(mixed=True)
|
||||||
|
print "hello",
|
||||||
|
print >>sys.stderr, "world",
|
||||||
|
print >>sys.stdout, ".",
|
||||||
|
out, err = cap.reset()
|
||||||
|
assert out.strip() == "hello world ."
|
||||||
|
assert not err
|
||||||
|
|
||||||
def test_capturing_twice_error(self):
|
def test_capturing_twice_error(self):
|
||||||
cap = self.getcapture()
|
cap = self.getcapture()
|
||||||
print "hello"
|
print "hello"
|
||||||
|
@ -101,6 +110,26 @@ class TestCapturing:
|
||||||
out1, err1 = cap1.reset()
|
out1, err1 = cap1.reset()
|
||||||
assert out1 == "cap1\n"
|
assert out1 == "cap1\n"
|
||||||
assert out2 == "cap2\n"
|
assert out2 == "cap2\n"
|
||||||
|
|
||||||
|
def test_just_out_capture(self):
|
||||||
|
cap = self.getcapture(out=True, err=False)
|
||||||
|
print >>sys.stdout, "hello"
|
||||||
|
print >>sys.stderr, "world"
|
||||||
|
out, err = cap.reset()
|
||||||
|
assert out == "hello\n"
|
||||||
|
assert not err
|
||||||
|
|
||||||
|
def test_just_err_capture(self):
|
||||||
|
cap = self.getcapture(out=False, err=True)
|
||||||
|
print >>sys.stdout, "hello"
|
||||||
|
print >>sys.stderr, "world"
|
||||||
|
out, err = cap.reset()
|
||||||
|
assert err == "world\n"
|
||||||
|
assert not out
|
||||||
|
|
||||||
|
class TestStdCaptureFD(TestStdCapture):
|
||||||
|
def getcapture(self, **kw):
|
||||||
|
return py.io.StdCaptureFD(**kw)
|
||||||
|
|
||||||
def test_intermingling(self):
|
def test_intermingling(self):
|
||||||
cap = self.getcapture()
|
cap = self.getcapture()
|
||||||
|
@ -114,32 +143,16 @@ class TestCapturing:
|
||||||
assert out == "123"
|
assert out == "123"
|
||||||
assert err == "abc"
|
assert err == "abc"
|
||||||
|
|
||||||
def test_callcapture():
|
def test_callcapture(self):
|
||||||
def func(x, y):
|
def func(x, y):
|
||||||
print x
|
print x
|
||||||
print >>py.std.sys.stderr, y
|
print >>py.std.sys.stderr, y
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
res, out, err = py.io.StdCaptureFD.call(func, 3, y=4)
|
res, out, err = py.io.StdCaptureFD.call(func, 3, y=4)
|
||||||
assert res == 42
|
assert res == 42
|
||||||
assert out.startswith("3")
|
assert out.startswith("3")
|
||||||
assert err.startswith("4")
|
assert err.startswith("4")
|
||||||
|
|
||||||
def test_just_out_capture():
|
|
||||||
cap = py.io.StdCaptureFD(out=True, err=False)
|
|
||||||
print >>sys.stdout, "hello"
|
|
||||||
print >>sys.stderr, "world"
|
|
||||||
out, err = cap.reset()
|
|
||||||
assert out == "hello\n"
|
|
||||||
assert not err
|
|
||||||
|
|
||||||
def test_just_err_capture():
|
|
||||||
cap = py.io.StdCaptureFD(out=False, err=True)
|
|
||||||
print >>sys.stdout, "hello"
|
|
||||||
print >>sys.stderr, "world"
|
|
||||||
out, err = cap.reset()
|
|
||||||
assert err == "world\n"
|
|
||||||
assert not out
|
|
||||||
|
|
||||||
def test_capture_no_sys():
|
def test_capture_no_sys():
|
||||||
cap = py.io.StdCaptureFD(patchsys=False)
|
cap = py.io.StdCaptureFD(patchsys=False)
|
||||||
|
@ -151,6 +164,15 @@ def test_capture_no_sys():
|
||||||
assert out == "1"
|
assert out == "1"
|
||||||
assert err == "2"
|
assert err == "2"
|
||||||
|
|
||||||
#class TestCapturingOnFDs(TestCapturingOnSys):
|
def test_callcapture_nofd():
|
||||||
# def getcapture(self):
|
def func(x, y):
|
||||||
# return Capture()
|
os.write(1, "hello")
|
||||||
|
os.write(2, "hello")
|
||||||
|
print x
|
||||||
|
print >>py.std.sys.stderr, y
|
||||||
|
return 42
|
||||||
|
|
||||||
|
res, out, err = py.io.StdCapture.call(func, 3, y=4)
|
||||||
|
assert res == 42
|
||||||
|
assert out.startswith("3")
|
||||||
|
assert err.startswith("4")
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
import os, sys
|
|
||||||
import py
|
|
||||||
|
|
||||||
class TestCapturingOnSys:
|
|
||||||
|
|
||||||
def getcapture(self):
|
|
||||||
return py.io.StdCapture()
|
|
||||||
|
|
||||||
def test_capturing_simple(self):
|
|
||||||
cap = self.getcapture()
|
|
||||||
print "hello world"
|
|
||||||
print >>sys.stderr, "hello error"
|
|
||||||
out, err = cap.reset()
|
|
||||||
assert out == "hello world\n"
|
|
||||||
assert err == "hello error\n"
|
|
||||||
|
|
||||||
def test_capturing_twice_error(self):
|
|
||||||
cap = self.getcapture()
|
|
||||||
print "hello"
|
|
||||||
cap.reset()
|
|
||||||
py.test.raises(AttributeError, "cap.reset()")
|
|
||||||
|
|
||||||
def test_capturing_modify_sysouterr_in_between(self):
|
|
||||||
oldout = sys.stdout
|
|
||||||
olderr = sys.stderr
|
|
||||||
cap = self.getcapture()
|
|
||||||
print "hello",
|
|
||||||
print >>sys.stderr, "world",
|
|
||||||
sys.stdout = py.std.StringIO.StringIO()
|
|
||||||
sys.stderr = py.std.StringIO.StringIO()
|
|
||||||
print "not seen"
|
|
||||||
print >>sys.stderr, "not seen"
|
|
||||||
out, err = cap.reset()
|
|
||||||
assert out == "hello"
|
|
||||||
assert err == "world"
|
|
||||||
assert sys.stdout == oldout
|
|
||||||
assert sys.stderr == olderr
|
|
||||||
|
|
||||||
def test_capturing_error_recursive(self):
|
|
||||||
cap1 = self.getcapture()
|
|
||||||
print "cap1"
|
|
||||||
cap2 = self.getcapture()
|
|
||||||
print "cap2"
|
|
||||||
out2, err2 = cap2.reset()
|
|
||||||
py.test.raises(AttributeError, "cap2.reset()")
|
|
||||||
out1, err1 = cap1.reset()
|
|
||||||
assert out1 == "cap1\n"
|
|
||||||
assert out2 == "cap2\n"
|
|
||||||
|
|
||||||
def test_reading_stdin_while_captured_doesnt_hang(self):
|
|
||||||
cap = self.getcapture()
|
|
||||||
try:
|
|
||||||
py.test.raises(IOError, raw_input)
|
|
||||||
finally:
|
|
||||||
cap.reset()
|
|
||||||
|
|
||||||
def test_callcapture_nofd():
|
|
||||||
def func(x, y):
|
|
||||||
print x
|
|
||||||
print >>py.std.sys.stderr, y
|
|
||||||
return 42
|
|
||||||
|
|
||||||
res, out, err = py.io.StdCapture.call(func, 3, y=4)
|
|
||||||
assert res == 42
|
|
||||||
assert out.startswith("3")
|
|
||||||
assert err.startswith("4")
|
|
||||||
|
|
Loading…
Reference in New Issue