[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:
hpk 2007-02-01 21:26:27 +01:00
parent 9ddca27edc
commit 17754c6fd9
3 changed files with 96 additions and 117 deletions

View File

@ -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

View File

@ -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")

View File

@ -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")