remove externally setting and dealing with "item.outerr" from capturing in favor of a direct interface for adding reporting sections to items.

* * *
refactor makereport implementation to avoid recursion with __multicall__
This commit is contained in:
holger krekel 2014-03-14 12:49:34 +01:00
parent cde970be69
commit b47fdbe0a7
7 changed files with 43 additions and 34 deletions

View File

@ -151,8 +151,6 @@ class CaptureManager:
def resumecapture_item(self, item): def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath) method = self._getmethod(item.config, item.fspath)
if not hasattr(item, 'outerr'):
item.outerr = ('', '') # we accumulate outerr on the item
return self.resumecapture(method) return self.resumecapture(method)
def resumecapture(self, method=None): def resumecapture(self, method=None):
@ -174,16 +172,10 @@ class CaptureManager:
self.deactivate_funcargs() self.deactivate_funcargs()
if hasattr(self, '_capturing'): if hasattr(self, '_capturing'):
method = self._capturing method = self._capturing
del self._capturing
cap = self._method2capture.get(method) cap = self._method2capture.get(method)
if cap is not None: if cap is not None:
outerr = cap.suspend() return cap.suspend()
del self._capturing
if item:
outerr = (item.outerr[0] + outerr[0],
item.outerr[1] + outerr[1])
return outerr
if hasattr(item, 'outerr'):
return item.outerr
return "", "" return "", ""
def activate_funcargs(self, pyfuncitem): def activate_funcargs(self, pyfuncitem):
@ -235,18 +227,14 @@ class CaptureManager:
self.suspendcapture() self.suspendcapture()
@pytest.mark.tryfirst @pytest.mark.tryfirst
def pytest_runtest_makereport(self, __multicall__, item, call): def pytest_runtest_makereport(self, item, call):
funcarg_outerr = self.deactivate_funcargs() funcarg_outerr = self.deactivate_funcargs()
rep = __multicall__.execute() out, err = self.suspendcapture(item)
outerr = self.suspendcapture(item)
if funcarg_outerr is not None: if funcarg_outerr is not None:
outerr = (outerr[0] + funcarg_outerr[0], out += funcarg_outerr[0]
outerr[1] + funcarg_outerr[1]) err += funcarg_outerr[1]
addouterr(rep, outerr) item.add_report_section(call.when, "out", out)
if not rep.passed or rep.when == "teardown": item.add_report_section(call.when, "err", err)
outerr = ('', '')
item.outerr = outerr
return rep
error_capsysfderror = "cannot use capsys and capfd at the same time" error_capsysfderror = "cannot use capsys and capfd at the same time"

View File

@ -108,12 +108,14 @@ class LogXML(object):
)) ))
def _write_captured_output(self, report): def _write_captured_output(self, report):
sec = dict(report.sections) for capname in ('out', 'err'):
for name in ('out', 'err'): allcontent = ""
content = sec.get("Captured std%s" % name) for name, content in report.get_sections("Captured std%s" %
if content: capname):
tag = getattr(Junit, 'system-'+name) allcontent += content
self.append(tag(bin_xml_escape(content))) if allcontent:
tag = getattr(Junit, 'system-'+capname)
self.append(tag(bin_xml_escape(allcontent)))
def append(self, obj): def append(self, obj):
self.tests[-1].append(obj) self.tests[-1].append(obj)

View File

@ -233,6 +233,7 @@ class Node(object):
# used for storing artificial fixturedefs for direct parametrization # used for storing artificial fixturedefs for direct parametrization
self._name2pseudofixturedef = {} self._name2pseudofixturedef = {}
#self.extrainit() #self.extrainit()
@property @property
@ -469,6 +470,14 @@ class Item(Node):
""" """
nextitem = None nextitem = None
def __init__(self, name, parent=None, config=None, session=None):
super(Item, self).__init__(name, parent, config, session)
self._report_sections = []
def add_report_section(self, when, key, content):
if content:
self._report_sections.append((when, key, content))
def reportinfo(self): def reportinfo(self):
return self.fspath, None, "" return self.fspath, None, ""

View File

@ -35,8 +35,8 @@ class pytestPDB:
if item is not None: if item is not None:
capman = item.config.pluginmanager.getplugin("capturemanager") capman = item.config.pluginmanager.getplugin("capturemanager")
out, err = capman.suspendcapture() out, err = capman.suspendcapture()
if hasattr(item, 'outerr'): #if hasattr(item, 'outerr'):
item.outerr = (item.outerr[0] + out, item.outerr[1] + err) # item.outerr = (item.outerr[0] + out, item.outerr[1] + err)
tw = py.io.TerminalWriter() tw = py.io.TerminalWriter()
tw.line() tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)") tw.sep(">", "PDB set_trace (IO-capturing turned off)")

View File

@ -178,6 +178,11 @@ class BaseReport(object):
except UnicodeEncodeError: except UnicodeEncodeError:
out.line("<unprintable longrepr>") out.line("<unprintable longrepr>")
def get_sections(self, prefix):
for name, content in self.sections:
if name.startswith(prefix):
yield prefix, content
passed = property(lambda x: x.outcome == "passed") passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed") failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped") skipped = property(lambda x: x.outcome == "skipped")
@ -191,6 +196,7 @@ def pytest_runtest_makereport(item, call):
duration = call.stop-call.start duration = call.stop-call.start
keywords = dict([(x,1) for x in item.keywords]) keywords = dict([(x,1) for x in item.keywords])
excinfo = call.excinfo excinfo = call.excinfo
sections = []
if not call.excinfo: if not call.excinfo:
outcome = "passed" outcome = "passed"
longrepr = None longrepr = None
@ -209,16 +215,18 @@ def pytest_runtest_makereport(item, call):
else: # exception in setup or teardown else: # exception in setup or teardown
longrepr = item._repr_failure_py(excinfo, longrepr = item._repr_failure_py(excinfo,
style=item.config.option.tbstyle) style=item.config.option.tbstyle)
for rwhen, key, content in item._report_sections:
sections.append(("Captured std%s %s" %(key, rwhen), content))
return TestReport(item.nodeid, item.location, return TestReport(item.nodeid, item.location,
keywords, outcome, longrepr, when, keywords, outcome, longrepr, when,
duration=duration) sections, duration)
class TestReport(BaseReport): class TestReport(BaseReport):
""" Basic test report object (also used for setup and teardown calls if """ Basic test report object (also used for setup and teardown calls if
they fail). they fail).
""" """
def __init__(self, nodeid, location, def __init__(self, nodeid, location, keywords, outcome,
keywords, outcome, longrepr, when, sections=(), duration=0, **extra): longrepr, when, sections=(), duration=0, **extra):
#: normalized collection node id #: normalized collection node id
self.nodeid = nodeid self.nodeid = nodeid

View File

@ -282,9 +282,9 @@ class TestPerTestCapturing:
"====* FAILURES *====", "====* FAILURES *====",
"____*____", "____*____",
"*test_capturing_outerr.py:8: ValueError", "*test_capturing_outerr.py:8: ValueError",
"*--- Captured stdout ---*", "*--- Captured stdout *call*",
"1", "1",
"*--- Captured stderr ---*", "*--- Captured stderr *call*",
"2", "2",
]) ])

View File

@ -478,10 +478,12 @@ def test_unicode_issue368(testdir):
path = testdir.tmpdir.join("test.xml") path = testdir.tmpdir.join("test.xml")
log = LogXML(str(path), None) log = LogXML(str(path), None)
ustr = py.builtin._totext("ВНИ!", "utf-8") ustr = py.builtin._totext("ВНИ!", "utf-8")
class report: from _pytest.runner import BaseReport
class Report(BaseReport):
longrepr = ustr longrepr = ustr
sections = [] sections = []
nodeid = "something" nodeid = "something"
report = Report()
# hopefully this is not too brittle ... # hopefully this is not too brittle ...
log.pytest_sessionstart() log.pytest_sessionstart()