From b47fdbe0a73666e65fa66df72c0ee0ce48c9dd03 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 14 Mar 2014 12:49:34 +0100 Subject: [PATCH] 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__ --- _pytest/capture.py | 28 ++++++++-------------------- _pytest/junitxml.py | 14 ++++++++------ _pytest/main.py | 9 +++++++++ _pytest/pdb.py | 4 ++-- _pytest/runner.py | 14 +++++++++++--- testing/test_capture.py | 4 ++-- testing/test_junitxml.py | 4 +++- 7 files changed, 43 insertions(+), 34 deletions(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index 12410026e..f5259f2be 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -151,8 +151,6 @@ class CaptureManager: def resumecapture_item(self, item): method = self._getmethod(item.config, item.fspath) - if not hasattr(item, 'outerr'): - item.outerr = ('', '') # we accumulate outerr on the item return self.resumecapture(method) def resumecapture(self, method=None): @@ -174,16 +172,10 @@ class CaptureManager: self.deactivate_funcargs() if hasattr(self, '_capturing'): method = self._capturing + del self._capturing cap = self._method2capture.get(method) if cap is not None: - outerr = 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 cap.suspend() return "", "" def activate_funcargs(self, pyfuncitem): @@ -235,18 +227,14 @@ class CaptureManager: self.suspendcapture() @pytest.mark.tryfirst - def pytest_runtest_makereport(self, __multicall__, item, call): + def pytest_runtest_makereport(self, item, call): funcarg_outerr = self.deactivate_funcargs() - rep = __multicall__.execute() - outerr = self.suspendcapture(item) + out, err = self.suspendcapture(item) if funcarg_outerr is not None: - outerr = (outerr[0] + funcarg_outerr[0], - outerr[1] + funcarg_outerr[1]) - addouterr(rep, outerr) - if not rep.passed or rep.when == "teardown": - outerr = ('', '') - item.outerr = outerr - return rep + out += funcarg_outerr[0] + err += funcarg_outerr[1] + item.add_report_section(call.when, "out", out) + item.add_report_section(call.when, "err", err) error_capsysfderror = "cannot use capsys and capfd at the same time" diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 2d330870f..c87e7f34f 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -108,12 +108,14 @@ class LogXML(object): )) def _write_captured_output(self, report): - sec = dict(report.sections) - for name in ('out', 'err'): - content = sec.get("Captured std%s" % name) - if content: - tag = getattr(Junit, 'system-'+name) - self.append(tag(bin_xml_escape(content))) + for capname in ('out', 'err'): + allcontent = "" + for name, content in report.get_sections("Captured std%s" % + capname): + allcontent += content + if allcontent: + tag = getattr(Junit, 'system-'+capname) + self.append(tag(bin_xml_escape(allcontent))) def append(self, obj): self.tests[-1].append(obj) diff --git a/_pytest/main.py b/_pytest/main.py index f7060bf6f..9ad861fe3 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -233,6 +233,7 @@ class Node(object): # used for storing artificial fixturedefs for direct parametrization self._name2pseudofixturedef = {} + #self.extrainit() @property @@ -469,6 +470,14 @@ class Item(Node): """ 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): return self.fspath, None, "" diff --git a/_pytest/pdb.py b/_pytest/pdb.py index 6405773f8..eef24e43e 100644 --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -35,8 +35,8 @@ class pytestPDB: if item is not None: capman = item.config.pluginmanager.getplugin("capturemanager") out, err = capman.suspendcapture() - if hasattr(item, 'outerr'): - item.outerr = (item.outerr[0] + out, item.outerr[1] + err) + #if hasattr(item, 'outerr'): + # item.outerr = (item.outerr[0] + out, item.outerr[1] + err) tw = py.io.TerminalWriter() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") diff --git a/_pytest/runner.py b/_pytest/runner.py index 539248117..ed3c8e9dc 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -178,6 +178,11 @@ class BaseReport(object): except UnicodeEncodeError: out.line("") + 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") failed = property(lambda x: x.outcome == "failed") skipped = property(lambda x: x.outcome == "skipped") @@ -191,6 +196,7 @@ def pytest_runtest_makereport(item, call): duration = call.stop-call.start keywords = dict([(x,1) for x in item.keywords]) excinfo = call.excinfo + sections = [] if not call.excinfo: outcome = "passed" longrepr = None @@ -209,16 +215,18 @@ def pytest_runtest_makereport(item, call): else: # exception in setup or teardown longrepr = item._repr_failure_py(excinfo, 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, keywords, outcome, longrepr, when, - duration=duration) + sections, duration) class TestReport(BaseReport): """ Basic test report object (also used for setup and teardown calls if they fail). """ - def __init__(self, nodeid, location, - keywords, outcome, longrepr, when, sections=(), duration=0, **extra): + def __init__(self, nodeid, location, keywords, outcome, + longrepr, when, sections=(), duration=0, **extra): #: normalized collection node id self.nodeid = nodeid diff --git a/testing/test_capture.py b/testing/test_capture.py index e3fe6cfcf..5e697fd2a 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -282,9 +282,9 @@ class TestPerTestCapturing: "====* FAILURES *====", "____*____", "*test_capturing_outerr.py:8: ValueError", - "*--- Captured stdout ---*", + "*--- Captured stdout *call*", "1", - "*--- Captured stderr ---*", + "*--- Captured stderr *call*", "2", ]) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 05573ad68..965c444bf 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -478,10 +478,12 @@ def test_unicode_issue368(testdir): path = testdir.tmpdir.join("test.xml") log = LogXML(str(path), None) ustr = py.builtin._totext("ВНИ!", "utf-8") - class report: + from _pytest.runner import BaseReport + class Report(BaseReport): longrepr = ustr sections = [] nodeid = "something" + report = Report() # hopefully this is not too brittle ... log.pytest_sessionstart()