[Pytest-commit] commit/pytest: 22 new changesets

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed Apr 2 09:17:50 CEST 2014


22 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/595633b5b497/
Changeset:   595633b5b497
User:        hpk42
Date:        2014-03-14 12:49:34
Summary:     remove unneccessary indirections and options
Affected #:  1 file

diff -r 52e080dd60eda8dcf4732baebebebb79160d14ff -r 595633b5b497a24d4c06896290ff4a0783ada19a _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -120,9 +120,6 @@
         f.close()
         return newf
 
-    def _makestringio(self):
-        return TextIO()
-
     def _getcapture(self, method):
         if method == "fd":
             return StdCaptureFD(
@@ -130,10 +127,7 @@
                 err=self._maketempfile(),
             )
         elif method == "sys":
-            return StdCapture(
-                out=self._makestringio(),
-                err=self._makestringio(),
-            )
+            return StdCapture(out=TextIO(), err=TextIO())
         elif method == "no":
             return NoCapture()
         else:


https://bitbucket.org/hpk42/pytest/commits/eef0bd2fa258/
Changeset:   eef0bd2fa258
User:        hpk42
Date:        2014-03-14 12:49:34
Summary:     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__
Affected #:  7 files

diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -151,8 +151,6 @@
 
     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 @@
         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 @@
             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 -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/junitxml.py
--- a/_pytest/junitxml.py
+++ b/_pytest/junitxml.py
@@ -108,12 +108,14 @@
         ))
 
     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 -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -233,6 +233,7 @@
 
         # used for storing artificial fixturedefs for direct parametrization
         self._name2pseudofixturedef = {}
+
         #self.extrainit()
 
     @property
@@ -469,6 +470,14 @@
     """
     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 -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/pdb.py
--- a/_pytest/pdb.py
+++ b/_pytest/pdb.py
@@ -35,8 +35,8 @@
         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 -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/runner.py
--- a/_pytest/runner.py
+++ b/_pytest/runner.py
@@ -178,6 +178,11 @@
             except UnicodeEncodeError:
                 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")
     failed = property(lambda x: x.outcome == "failed")
     skipped = property(lambda x: x.outcome == "skipped")
@@ -191,6 +196,7 @@
     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 @@
             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 -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -282,9 +282,9 @@
             "====* FAILURES *====",
             "____*____",
             "*test_capturing_outerr.py:8: ValueError",
-            "*--- Captured stdout ---*",
+            "*--- Captured stdout *call*",
             "1",
-            "*--- Captured stderr ---*",
+            "*--- Captured stderr *call*",
             "2",
         ])
 

diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c testing/test_junitxml.py
--- a/testing/test_junitxml.py
+++ b/testing/test_junitxml.py
@@ -478,10 +478,12 @@
     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()


https://bitbucket.org/hpk42/pytest/commits/d3abe2e5e194/
Changeset:   d3abe2e5e194
User:        hpk42
Date:        2014-03-14 12:49:35
Summary:     implement a new hook type: hook wrappers using a "yield" to distinguish
between working at the front and at the end of a hook call chain.
The idea is to make it easier for a plugin to "wrap" a certain hook
call and use context managers, in particular allow a major cleanup of
capturing.
Affected #:  2 files

diff -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 _pytest/core.py
--- a/_pytest/core.py
+++ b/_pytest/core.py
@@ -240,18 +240,22 @@
             pass
         l = []
         last = []
+        wrappers = []
         for plugin in plugins:
             try:
                 meth = getattr(plugin, attrname)
-                if hasattr(meth, 'tryfirst'):
-                    last.append(meth)
-                elif hasattr(meth, 'trylast'):
-                    l.insert(0, meth)
-                else:
-                    l.append(meth)
             except AttributeError:
                 continue
+            if hasattr(meth, 'hookwrapper'):
+                wrappers.append(meth)
+            elif hasattr(meth, 'tryfirst'):
+                last.append(meth)
+            elif hasattr(meth, 'trylast'):
+                l.insert(0, meth)
+            else:
+                l.append(meth)
         l.extend(last)
+        l.extend(wrappers)
         self._listattrcache[key] = list(l)
         return l
 
@@ -272,6 +276,14 @@
 
 class MultiCall:
     """ execute a call into multiple python functions/methods. """
+
+    class WrongHookWrapper(Exception):
+        """ a hook wrapper does not behave correctly. """
+        def __init__(self, func, message):
+            Exception.__init__(self, func, message)
+            self.func = func
+            self.message = message
+
     def __init__(self, methods, kwargs, firstresult=False):
         self.methods = list(methods)
         self.kwargs = kwargs
@@ -283,16 +295,39 @@
         return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
 
     def execute(self):
-        while self.methods:
-            method = self.methods.pop()
-            kwargs = self.getkwargs(method)
-            res = method(**kwargs)
-            if res is not None:
-                self.results.append(res)
-                if self.firstresult:
-                    return res
-        if not self.firstresult:
-            return self.results
+        next_finalizers = []
+        try:
+            while self.methods:
+                method = self.methods.pop()
+                kwargs = self.getkwargs(method)
+                if hasattr(method, "hookwrapper"):
+                    it = method(**kwargs)
+                    next = getattr(it, "next", None)
+                    if next is None:
+                        next = getattr(it, "__next__", None)
+                        if next is None:
+                            raise self.WrongHookWrapper(method,
+                                "wrapper does not contain a yield")
+                    res = next()
+                    next_finalizers.append((method, next))
+                else:
+                    res = method(**kwargs)
+                if res is not None:
+                    self.results.append(res)
+                    if self.firstresult:
+                        return res
+            if not self.firstresult:
+                return self.results
+        finally:
+            for method, fin in reversed(next_finalizers):
+                try:
+                    fin()
+                except StopIteration:
+                    pass
+                else:
+                    raise self.WrongHookWrapper(method,
+                                "wrapper contain more than one yield")
+
 
     def getkwargs(self, method):
         kwargs = {}

diff -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 testing/test_core.py
--- a/testing/test_core.py
+++ b/testing/test_core.py
@@ -523,6 +523,95 @@
         res = MultiCall([m1, m2], {}).execute()
         assert res == [1]
 
+    def test_hookwrapper(self):
+        l = []
+        def m1():
+            l.append("m1 init")
+            yield None
+            l.append("m1 finish")
+        m1.hookwrapper = True
+
+        def m2():
+            l.append("m2")
+            return 2
+        res = MultiCall([m2, m1], {}).execute()
+        assert res == [2]
+        assert l == ["m1 init", "m2", "m1 finish"]
+        l[:] = []
+        res = MultiCall([m2, m1], {}, firstresult=True).execute()
+        assert res == 2
+        assert l == ["m1 init", "m2", "m1 finish"]
+
+    def test_hookwrapper_order(self):
+        l = []
+        def m1():
+            l.append("m1 init")
+            yield 1
+            l.append("m1 finish")
+        m1.hookwrapper = True
+
+        def m2():
+            l.append("m2 init")
+            yield 2
+            l.append("m2 finish")
+        m2.hookwrapper = True
+        res = MultiCall([m2, m1], {}).execute()
+        assert res == [1, 2]
+        assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
+
+    def test_listattr_hookwrapper_ordering(self):
+        class P1:
+            @pytest.mark.hookwrapper
+            def m(self):
+                return 17
+
+        class P2:
+            def m(self):
+                return 23
+
+        class P3:
+            @pytest.mark.tryfirst
+            def m(self):
+                return 19
+
+        pluginmanager = PluginManager()
+        p1 = P1()
+        p2 = P2()
+        p3 = P3()
+        pluginmanager.register(p1)
+        pluginmanager.register(p2)
+        pluginmanager.register(p3)
+        methods = pluginmanager.listattr('m')
+        assert methods == [p2.m, p3.m, p1.m]
+        ## listattr keeps a cache and deleting
+        ## a function attribute requires clearing it
+        #pluginmanager._listattrcache.clear()
+        #del P1.m.__dict__['tryfirst']
+
+    def test_hookwrapper_not_yield(self):
+        def m1():
+            pass
+        m1.hookwrapper = True
+
+        mc = MultiCall([m1], {})
+        with pytest.raises(mc.WrongHookWrapper) as ex:
+            mc.execute()
+        assert ex.value.func == m1
+        assert ex.value.message
+
+    def test_hookwrapper_too_many_yield(self):
+        def m1():
+            yield 1
+            yield 2
+        m1.hookwrapper = True
+
+        mc = MultiCall([m1], {})
+        with pytest.raises(mc.WrongHookWrapper) as ex:
+            mc.execute()
+        assert ex.value.func == m1
+        assert ex.value.message
+
+
 class TestHookRelay:
     def test_happypath(self):
         pm = PluginManager()


https://bitbucket.org/hpk42/pytest/commits/cfac82eca2e8/
Changeset:   cfac82eca2e8
User:        hpk42
Date:        2014-03-14 12:49:36
Summary:     - turn on capturing before early conftest loading and make terminal writer
  use the original stream.

- avoid resetting capture FDs/sys.stdout for each test by keeping capturing
  always turned on and looking at snapshotted capturing data during runtest
  and collection phases.
Affected #:  11 files

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -1,12 +1,11 @@
 """
-    per-test stdout/stderr capturing mechanisms,
-    ``capsys`` and ``capfd`` function arguments.
+per-test stdout/stderr capturing mechanism.
+
 """
-# note: py.io capture was where copied from
-# pylib 1.4.20.dev2 (rev 13d9af95547e)
 import sys
 import os
 import tempfile
+import contextlib
 
 import py
 import pytest
@@ -58,8 +57,18 @@
         method = "fd"
     if method == "fd" and not hasattr(os, "dup"):
         method = "sys"
+    pluginmanager = early_config.pluginmanager
+    if method != "no":
+        try:
+            sys.stdout.fileno()
+        except Exception:
+            dupped_stdout = sys.stdout
+        else:
+            dupped_stdout = dupfile(sys.stdout, buffering=1)
+        pluginmanager.register(dupped_stdout, "dupped_stdout")
+            #pluginmanager.add_shutdown(dupped_stdout.close)
     capman = CaptureManager(method)
-    early_config.pluginmanager.register(capman, "capturemanager")
+    pluginmanager.register(capman, "capturemanager")
 
     # make sure that capturemanager is properly reset at final shutdown
     def teardown():
@@ -68,13 +77,13 @@
         except ValueError:
             pass
 
-    early_config.pluginmanager.add_shutdown(teardown)
+    pluginmanager.add_shutdown(teardown)
 
     # make sure logging does not raise exceptions at the end
     def silence_logging_at_shutdown():
         if "logging" in sys.modules:
             sys.modules["logging"].raiseExceptions = False
-    early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown)
+    pluginmanager.add_shutdown(silence_logging_at_shutdown)
 
     # finally trigger conftest loading but while capturing (issue93)
     capman.resumecapture()
@@ -89,23 +98,20 @@
         raise
 
 
-def addouterr(rep, outerr):
-    for secname, content in zip(["out", "err"], outerr):
-        if content:
-            rep.sections.append(("Captured std%s" % secname, content))
-
-
 class NoCapture:
-    def startall(self):
+    def start_capturing(self):
         pass
 
-    def resume(self):
+    def stop_capturing(self):
+        pass
+
+    def pop_outerr_to_orig(self):
         pass
 
     def reset(self):
         pass
 
-    def suspend(self):
+    def readouterr(self):
         return "", ""
 
 
@@ -147,7 +153,9 @@
 
     def reset_capturings(self):
         for cap in self._method2capture.values():
+            cap.pop_outerr_to_orig()
             cap.reset()
+        self._method2capture.clear()
 
     def resumecapture_item(self, item):
         method = self._getmethod(item.config, item.fspath)
@@ -164,9 +172,9 @@
         self._capturing = method
         if cap is None:
             self._method2capture[method] = cap = self._getcapture(method)
-            cap.startall()
+            cap.start_capturing()
         else:
-            cap.resume()
+            cap.pop_outerr_to_orig()
 
     def suspendcapture(self, item=None):
         self.deactivate_funcargs()
@@ -175,7 +183,7 @@
             del self._capturing
             cap = self._method2capture.get(method)
             if cap is not None:
-                return cap.suspend()
+                return cap.readouterr()
         return "", ""
 
     def activate_funcargs(self, pyfuncitem):
@@ -194,47 +202,68 @@
             del self._capturing_funcarg
             return outerr
 
+    @pytest.mark.hookwrapper
     def pytest_make_collect_report(self, __multicall__, collector):
         method = self._getmethod(collector.config, collector.fspath)
         try:
             self.resumecapture(method)
         except ValueError:
+            yield
             # recursive collect, XXX refactor capturing
             # to allow for more lightweight recursive capturing
             return
-        try:
-            rep = __multicall__.execute()
-        finally:
-            outerr = self.suspendcapture()
-        addouterr(rep, outerr)
-        return rep
+        yield
+        out, err = self.suspendcapture()
+        # XXX getting the report from the ongoing hook call is a bit
+        # of a hack.  We need to think about capturing during collection
+        # and find out if it's really needed fine-grained (per
+        # collector).
+        if __multicall__.results:
+            rep = __multicall__.results[0]
+            if out:
+                rep.sections.append(("Captured stdout", out))
+            if err:
+                rep.sections.append(("Captured stderr", err))
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_setup(self, item):
+        with self.item_capture_wrapper(item, "setup"):
+            yield
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_call(self, item):
+        with self.item_capture_wrapper(item, "call"):
+            yield
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_teardown(self, item):
+        with self.item_capture_wrapper(item, "teardown"):
+            yield
 
     @pytest.mark.tryfirst
-    def pytest_runtest_setup(self, item):
-        self.resumecapture_item(item)
+    def pytest_keyboard_interrupt(self, excinfo):
+        self.reset_capturings()
 
     @pytest.mark.tryfirst
-    def pytest_runtest_call(self, item):
+    def pytest_internalerror(self, excinfo):
+        self.reset_capturings()
+
+    @contextlib.contextmanager
+    def item_capture_wrapper(self, item, when):
         self.resumecapture_item(item)
-        self.activate_funcargs(item)
-
-    @pytest.mark.tryfirst
-    def pytest_runtest_teardown(self, item):
-        self.resumecapture_item(item)
-
-    def pytest_keyboard_interrupt(self, excinfo):
-        if hasattr(self, '_capturing'):
-            self.suspendcapture()
-
-    @pytest.mark.tryfirst
-    def pytest_runtest_makereport(self, item, call):
-        funcarg_outerr = self.deactivate_funcargs()
+        if when == "call":
+            self.activate_funcargs(item)
+            yield
+            funcarg_outerr = self.deactivate_funcargs()
+        else:
+            yield
+            funcarg_outerr = None
         out, err = self.suspendcapture(item)
         if funcarg_outerr is not None:
             out += funcarg_outerr[0]
             err += funcarg_outerr[1]
-        item.add_report_section(call.when, "out", out)
-        item.add_report_section(call.when, "err", err)
+        item.add_report_section(when, "out", out)
+        item.add_report_section(when, "err", err)
 
 error_capsysfderror = "cannot use capsys and capfd at the same time"
 
@@ -263,10 +292,10 @@
 
 class CaptureFixture:
     def __init__(self, captureclass):
-        self._capture = captureclass()
+        self._capture = captureclass(in_=False)
 
     def _start(self):
-        self._capture.startall()
+        self._capture.start_capturing()
 
     def _finalize(self):
         if hasattr(self, '_capture'):
@@ -295,6 +324,8 @@
         """
         self.targetfd = targetfd
         if tmpfile is None and targetfd != 0:
+            # this code path is covered in the tests
+            # but not used by a regular pytest run
             f = tempfile.TemporaryFile('wb+')
             tmpfile = dupfile(f, encoding="UTF-8")
             f.close()
@@ -390,13 +421,13 @@
         return getattr(self._stream, name)
 
 
-class Capture(object):
+class StdCaptureBase(object):
     def reset(self):
         """ reset sys.stdout/stderr and return captured output as strings. """
         if hasattr(self, '_reset'):
             raise ValueError("was already reset")
         self._reset = True
-        outfile, errfile = self.done(save=False)
+        outfile, errfile = self.stop_capturing(save=False)
         out, err = "", ""
         if outfile and not outfile.closed:
             out = outfile.read()
@@ -406,14 +437,16 @@
             errfile.close()
         return out, err
 
-    def suspend(self):
-        """ return current snapshot captures, memorize tempfiles. """
-        outerr = self.readouterr()
-        outfile, errfile = self.done()
-        return outerr
+    def pop_outerr_to_orig(self):
+        """ pop current snapshot out/err capture and flush to orig streams. """
+        out, err = self.readouterr()
+        if out:
+            self.out.writeorg(out)
+        if err:
+            self.err.writeorg(err)
 
 
-class StdCaptureFD(Capture):
+class StdCaptureFD(StdCaptureBase):
     """ This class allows to capture writes to FD1 and FD2
         and may connect a NULL file to FD0 (and prevent
         reads from sys.stdin).  If any of the 0,1,2 file descriptors
@@ -464,7 +497,7 @@
             except OSError:
                 pass
 
-    def startall(self):
+    def start_capturing(self):
         if hasattr(self, 'in_'):
             self.in_.start()
         if hasattr(self, 'out'):
@@ -472,11 +505,10 @@
         if hasattr(self, 'err'):
             self.err.start()
 
-    def resume(self):
-        """ resume capturing with original temp files. """
-        self.startall()
+    #def pytest_sessionfinish(self):
+    #    self.reset_capturings()
 
-    def done(self, save=True):
+    def stop_capturing(self, save=True):
         """ return (outfile, errfile) and stop capturing. """
         outfile = errfile = None
         if hasattr(self, 'out') and not self.out.tmpfile.closed:
@@ -491,16 +523,15 @@
 
     def readouterr(self):
         """ return snapshot value of stdout/stderr capturings. """
-        out = self._readsnapshot('out')
-        err = self._readsnapshot('err')
-        return out, err
+        return self._readsnapshot('out'), self._readsnapshot('err')
 
     def _readsnapshot(self, name):
-        if hasattr(self, name):
+        try:
             f = getattr(self, name).tmpfile
-        else:
+        except AttributeError:
             return ''
-
+        if f.tell() == 0:
+            return ''
         f.seek(0)
         res = f.read()
         enc = getattr(f, "encoding", None)
@@ -510,8 +541,17 @@
         f.seek(0)
         return res
 
+class TextCapture(TextIO):
+    def __init__(self, oldout):
+        super(TextCapture, self).__init__()
+        self._oldout = oldout
 
-class StdCapture(Capture):
+    def writeorg(self, data):
+        self._oldout.write(data)
+        self._oldout.flush()
+
+
+class StdCapture(StdCaptureBase):
     """ This class allows to capture writes to sys.stdout|stderr "in-memory"
         and will raise errors on tries to read from sys.stdin. It only
         modifies sys.stdout|stderr|stdin attributes and does not
@@ -522,15 +562,15 @@
         self._olderr = sys.stderr
         self._oldin = sys.stdin
         if out and not hasattr(out, 'file'):
-            out = TextIO()
+            out = TextCapture(self._oldout)
         self.out = out
         if err:
             if not hasattr(err, 'write'):
-                err = TextIO()
+                err = TextCapture(self._olderr)
         self.err = err
         self.in_ = in_
 
-    def startall(self):
+    def start_capturing(self):
         if self.out:
             sys.stdout = self.out
         if self.err:
@@ -538,7 +578,7 @@
         if self.in_:
             sys.stdin = self.in_ = DontReadFromInput()
 
-    def done(self, save=True):
+    def stop_capturing(self, save=True):
         """ return (outfile, errfile) and stop capturing. """
         outfile = errfile = None
         if self.out and not self.out.closed:
@@ -553,9 +593,6 @@
             sys.stdin = self._oldin
         return outfile, errfile
 
-    def resume(self):
-        """ resume capturing with original temp files. """
-        self.startall()
 
     def readouterr(self):
         """ return snapshot value of stdout/stderr capturings. """

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -56,11 +56,15 @@
             raise ValueError("not a string or argument list: %r" % (args,))
         args = py.std.shlex.split(args)
     pluginmanager = get_plugin_manager()
-    if plugins:
-        for plugin in plugins:
-            pluginmanager.register(plugin)
-    return pluginmanager.hook.pytest_cmdline_parse(
-            pluginmanager=pluginmanager, args=args)
+    try:
+        if plugins:
+            for plugin in plugins:
+                pluginmanager.register(plugin)
+        return pluginmanager.hook.pytest_cmdline_parse(
+                pluginmanager=pluginmanager, args=args)
+    except Exception:
+        pluginmanager.ensure_shutdown()
+        raise
 
 class PytestPluginManager(PluginManager):
     def __init__(self, hookspecs=[hookspec]):
@@ -612,6 +616,9 @@
         self.hook.pytest_logwarning(code=code, message=message,
                                     fslocation=None, nodeid=None)
 
+    def get_terminal_writer(self):
+        return self.pluginmanager.getplugin("terminalreporter")._tw
+
     def pytest_cmdline_parse(self, pluginmanager, args):
         assert self == pluginmanager.config, (self, pluginmanager.config)
         self.parse(args)

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -60,6 +60,7 @@
 def pytest_cmdline_main(config):
     genscript = config.getvalue("genscript")
     if genscript:
+        #tw = config.get_terminal_writer()
         tw = py.io.TerminalWriter()
         deps =  ['py', '_pytest', 'pytest']
         if sys.version_info < (2,7):

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -47,6 +47,8 @@
 
 def pytest_cmdline_main(config):
     if config.option.version:
+        capman = config.pluginmanager.getplugin("capturemanager")
+        capman.reset_capturings()
         p = py.path.local(pytest.__file__)
         sys.stderr.write("This is pytest version %s, imported from %s\n" %
             (pytest.__version__, p))
@@ -62,7 +64,7 @@
         return 0
 
 def showhelp(config):
-    tw = py.io.TerminalWriter()
+    tw = config.get_terminal_writer()
     tw.write(config._parser.optparser.format_help())
     tw.line()
     tw.line()

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/mark.py
--- a/_pytest/mark.py
+++ b/_pytest/mark.py
@@ -40,7 +40,7 @@
 def pytest_cmdline_main(config):
     if config.option.markers:
         config.do_configure()
-        tw = py.io.TerminalWriter()
+        tw = config.get_terminal_writer()
         for line in config.getini("markers"):
             name, rest = line.split(":", 1)
             tw.write("@pytest.mark.%s:" % name, bold=True)

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/pdb.py
--- a/_pytest/pdb.py
+++ b/_pytest/pdb.py
@@ -34,10 +34,9 @@
 
         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)
-            tw = py.io.TerminalWriter()
+            if capman:
+                capman.reset_capturings()
+            tw = item.config.get_terminal_writer()
             tw.line()
             tw.sep(">", "PDB set_trace (IO-capturing turned off)")
         py.std.pdb.Pdb().set_trace(frame)
@@ -46,19 +45,20 @@
     pytestPDB.item = item
 pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
 
- at pytest.mark.tryfirst
-def pytest_make_collect_report(__multicall__, collector):
-    try:
-        pytestPDB.collector = collector
-        return __multicall__.execute()
-    finally:
-        pytestPDB.collector = None
+ at pytest.mark.hookwrapper
+def pytest_make_collect_report(collector):
+    pytestPDB.collector = collector
+    yield
+    pytestPDB.collector = None
 
 def pytest_runtest_makereport():
     pytestPDB.item = None
 
 class PdbInvoke:
     def pytest_exception_interact(self, node, call, report):
+        capman = node.config.pluginmanager.getplugin("capturemanager")
+        if capman:
+            capman.reset_capturings()
         return _enter_pdb(node, call.excinfo, report)
 
     def pytest_internalerror(self, excrepr, excinfo):

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -885,7 +885,7 @@
         nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:]))
         nodeid.replace(session.fspath.sep, "/")
 
-    tw = py.io.TerminalWriter()
+    tw = config.get_terminal_writer()
     verbose = config.getvalue("verbose")
 
     fm = session._fixturemanager

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/runner.py
--- a/_pytest/runner.py
+++ b/_pytest/runner.py
@@ -135,14 +135,13 @@
         self.when = when
         self.start = time()
         try:
-            try:
-                self.result = func()
-            except KeyboardInterrupt:
-                raise
-            except:
-                self.excinfo = py.code.ExceptionInfo()
-        finally:
+            self.result = func()
+        except KeyboardInterrupt:
             self.stop = time()
+            raise
+        except:
+            self.excinfo = py.code.ExceptionInfo()
+        self.stop = time()
 
     def __repr__(self):
         if self.excinfo:
@@ -292,7 +291,8 @@
 
 
 class CollectReport(BaseReport):
-    def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
+    def __init__(self, nodeid, outcome, longrepr, result,
+                 sections=(), **extra):
         self.nodeid = nodeid
         self.outcome = outcome
         self.longrepr = longrepr

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -36,7 +36,10 @@
 
 def pytest_configure(config):
     config.option.verbose -= config.option.quiet
-    reporter = TerminalReporter(config, sys.stdout)
+    out = config.pluginmanager.getplugin("dupped_stdout")
+    #if out is None:
+    #    out = sys.stdout
+    reporter = TerminalReporter(config, out)
     config.pluginmanager.register(reporter, 'terminalreporter')
     if config.option.debug or config.option.traceconfig:
         def mywriter(tags, args):
@@ -44,6 +47,11 @@
             reporter.write_line("[traceconfig] " + msg)
         config.trace.root.setprocessor("pytest:config", mywriter)
 
+def get_terminal_writer(config):
+    tr = config.pluginmanager.getplugin("terminalreporter")
+    return tr._tw
+
+
 def getreportopt(config):
     reportopts = ""
     optvalue = config.option.report

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 bench/bench.py
--- a/bench/bench.py
+++ b/bench/bench.py
@@ -4,8 +4,8 @@
     import cProfile
     import pytest
     import pstats
-    script = sys.argv[1] if len(sys.argv) > 1 else "empty.py"
-    stats = cProfile.run('pytest.cmdline.main([%r])' % script, 'prof')
+    script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py"
+    stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof')
     p = pstats.Stats("prof")
     p.strip_dirs()
     p.sort_stats('cumulative')

diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -736,14 +736,14 @@
 class TestStdCapture:
     def getcapture(self, **kw):
         cap = capture.StdCapture(**kw)
-        cap.startall()
+        cap.start_capturing()
         return cap
 
     def test_capturing_done_simple(self):
         cap = self.getcapture()
         sys.stdout.write("hello")
         sys.stderr.write("world")
-        outfile, errfile = cap.done()
+        outfile, errfile = cap.stop_capturing()
         s = outfile.read()
         assert s == "hello"
         s = errfile.read()
@@ -787,6 +787,7 @@
         print('\xa6')
         out, err = cap.readouterr()
         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
+        cap.reset()
 
     def test_reset_twice_error(self):
         cap = self.getcapture()
@@ -859,12 +860,13 @@
         try:
             print ("hello")
             sys.stderr.write("error\n")
-            out, err = cap.suspend()
+            out, err = cap.readouterr()
+            cap.stop_capturing()
             assert out == "hello\n"
             assert not err
             print ("in between")
             sys.stderr.write("in between\n")
-            cap.resume()
+            cap.start_capturing()
             print ("after")
             sys.stderr.write("error_after\n")
         finally:
@@ -878,7 +880,7 @@
 
     def getcapture(self, **kw):
         cap = capture.StdCaptureFD(**kw)
-        cap.startall()
+        cap.start_capturing()
         return cap
 
     def test_intermingling(self):
@@ -908,7 +910,7 @@
     try:
         os.write(1, "hello".encode("ascii"))
         os.write(2, "world".encode("ascii"))
-        outf, errf = capfd.done()
+        outf, errf = capfd.stop_capturing()
     finally:
         capfd.reset()
     assert outf == tmpfile
@@ -924,15 +926,15 @@
             def test_stdout():
                 os.close(1)
                 cap = StdCaptureFD(out=True, err=False, in_=False)
-                cap.done()
+                cap.stop_capturing()
             def test_stderr():
                 os.close(2)
                 cap = StdCaptureFD(out=False, err=True, in_=False)
-                cap.done()
+                cap.stop_capturing()
             def test_stdin():
                 os.close(0)
                 cap = StdCaptureFD(out=False, err=False, in_=True)
-                cap.done()
+                cap.stop_capturing()
         """)
         result = testdir.runpytest("--capture=fd")
         assert result.ret == 0
@@ -941,8 +943,8 @@
 
 def test_capture_not_started_but_reset():
     capsys = capture.StdCapture()
-    capsys.done()
-    capsys.done()
+    capsys.stop_capturing()
+    capsys.stop_capturing()
     capsys.reset()
 
 
@@ -951,7 +953,7 @@
     capsys = capture.StdCapture()
     try:
         cap = capture.StdCaptureFD(patchsys=False)
-        cap.startall()
+        cap.start_capturing()
         sys.stdout.write("hello")
         sys.stderr.write("world")
         oswritebytes(1, "1")
@@ -970,10 +972,9 @@
         tmpfile = True
     cap = capture.StdCaptureFD(out=False, err=tmpfile)
     try:
-        cap.startall()
+        cap.start_capturing()
         capfile = cap.err.tmpfile
-        cap.suspend()
-        cap.resume()
+        cap.readouterr()
     finally:
         cap.reset()
     capfile2 = cap.err.tmpfile
@@ -990,22 +991,25 @@
         import py, logging
         from _pytest import capture
         cap = capture.%s(out=False, in_=False)
-        cap.startall()
+        cap.start_capturing()
 
         logging.warn("hello1")
-        outerr = cap.suspend()
+        outerr = cap.readouterr()
         print ("suspend, captured %%s" %%(outerr,))
         logging.warn("hello2")
 
-        cap.resume()
+        cap.pop_outerr_to_orig()
         logging.warn("hello3")
 
-        outerr = cap.suspend()
+        outerr = cap.readouterr()
         print ("suspend2, captured %%s" %% (outerr,))
     """ % (method,))
     result = testdir.runpython(p)
-    result.stdout.fnmatch_lines([
-        "suspend, captured*hello1*",
-        "suspend2, captured*hello2*WARNING:root:hello3*",
-    ])
+    result.stdout.fnmatch_lines("""
+        suspend, captured*hello1*
+        suspend2, captured*WARNING:root:hello3*
+    """)
+    result.stderr.fnmatch_lines("""
+        WARNING:root:hello2
+    """)
     assert "atexit" not in result.stderr.str()


https://bitbucket.org/hpk42/pytest/commits/d774e7c11ee6/
Changeset:   d774e7c11ee6
User:        hpk42
Date:        2014-03-14 12:49:37
Summary:     simplify pdb disabling of capturing, also accomodate the new semantics
that capturing is always on during a test session.
Affected #:  1 file

diff -r cfac82eca2e810f0c6bf74945981f57e66d1d316 -r d774e7c11ee60f641f360b366d2f4d8165ec8ed1 _pytest/pdb.py
--- a/_pytest/pdb.py
+++ b/_pytest/pdb.py
@@ -16,43 +16,30 @@
     if config.getvalue("usepdb"):
         config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
 
-    old_trace = py.std.pdb.set_trace
+    old = (py.std.pdb.set_trace, pytestPDB._pluginmanager)
     def fin():
-        py.std.pdb.set_trace = old_trace
+        py.std.pdb.set_trace, pytestPDB._pluginmanager = old
     py.std.pdb.set_trace = pytest.set_trace
+    pytestPDB._pluginmanager = config.pluginmanager
     config._cleanup.append(fin)
 
 class pytestPDB:
     """ Pseudo PDB that defers to the real pdb. """
-    item = None
-    collector = None
+    _pluginmanager = None
 
     def set_trace(self):
         """ invoke PDB set_trace debugging, dropping any IO capturing. """
         frame = sys._getframe().f_back
-        item = self.item or self.collector
-
-        if item is not None:
-            capman = item.config.pluginmanager.getplugin("capturemanager")
+        capman = None
+        if self._pluginmanager is not None:
+            capman = self._pluginmanager.getplugin("capturemanager")
             if capman:
                 capman.reset_capturings()
-            tw = item.config.get_terminal_writer()
+            tw = py.io.TerminalWriter()
             tw.line()
             tw.sep(">", "PDB set_trace (IO-capturing turned off)")
         py.std.pdb.Pdb().set_trace(frame)
 
-def pdbitem(item):
-    pytestPDB.item = item
-pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
-
- at pytest.mark.hookwrapper
-def pytest_make_collect_report(collector):
-    pytestPDB.collector = collector
-    yield
-    pytestPDB.collector = None
-
-def pytest_runtest_makereport():
-    pytestPDB.item = None
 
 class PdbInvoke:
     def pytest_exception_interact(self, node, call, report):


https://bitbucket.org/hpk42/pytest/commits/25c6de59b09b/
Changeset:   25c6de59b09b
User:        hpk42
Date:        2014-03-14 15:58:16
Summary:     tentatively fix py33 and py25 compat
Affected #:  1 file

diff -r d774e7c11ee60f641f360b366d2f4d8165ec8ed1 -r 25c6de59b09b45455976fa3139cdff360d95b82c _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -2,6 +2,8 @@
 per-test stdout/stderr capturing mechanism.
 
 """
+from __future__ import with_statement
+
 import sys
 import os
 import tempfile
@@ -366,12 +368,9 @@
     def writeorg(self, data):
         """ write a string to the original file descriptor
         """
-        tempfp = tempfile.TemporaryFile()
-        try:
-            os.dup2(self._savefd, tempfp.fileno())
-            tempfp.write(data)
-        finally:
-            tempfp.close()
+        if py.builtin._istext(data):
+            data = data.encode("utf8") # XXX use encoding of original stream
+        os.write(self._savefd, data)
 
 
 def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):


https://bitbucket.org/hpk42/pytest/commits/b36800a30a51/
Changeset:   b36800a30a51
User:        hpk42
Date:        2014-03-27 13:57:54
Summary:     merge in current default
Affected #:  22 files

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -9,6 +9,7 @@
 bin/
 include/
 .Python/
+.env/
 
 # These lines are suggested according to the svn:ignore property
 # Feel free to enable them by uncommenting them
@@ -27,6 +28,7 @@
 *.egg-info
 issue/
 env/
+env3/
 3rdparty/
 .tox
 .cache

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 AUTHORS
--- a/AUTHORS
+++ b/AUTHORS
@@ -38,3 +38,5 @@
 Mark Abramowitz
 Piotr Banaszkiewicz
 Jurko Gospodnetić
+Marc Schlaich
+Christopher Gilling

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,6 +11,19 @@
 - change skips into warnings for test classes with an __init__ and
   callables in test modules which look like a test but are not functions.
 
+- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions
+  during collection/loading of test modules.  Thanks to Marc Schlaich
+  for the complete PR.
+
+- fix issue490: include pytest_load_initial_conftests in documentation
+  and improve docstring.
+
+- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work
+  if it's triggered ahead of command line parsing.
+
+- merge PR123: improved integration with mock.patch decorator on tests.
+
+
 2.5.2
 -----------------------------------
 

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -440,7 +440,7 @@
             if len(option) == 2 or option[2] == ' ':
                 return_list.append(option)
             if option[2:] == short_long.get(option.replace('-', '')):
-                return_list.append(option)
+                return_list.append(option.replace(' ', '='))
         action._formatted_action_invocation = ', '.join(return_list)
         return action._formatted_action_invocation
 

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -53,8 +53,8 @@
 pytest_cmdline_main.firstresult = True
 
 def pytest_load_initial_conftests(args, early_config, parser):
-    """ implements loading initial conftests.
-    """
+    """ implements the loading of initial conftest files ahead
+    of command line option parsing. """
 
 def pytest_configure(config):
     """ called after command line options have been parsed

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -8,7 +8,7 @@
 except ImportError:
     from UserDict import DictMixin as MappingMixin
 
-from _pytest.runner import collect_one_node, Skipped
+from _pytest.runner import collect_one_node
 
 tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
 
@@ -409,10 +409,6 @@
         and thus iteratively build a tree.
     """
 
-    # the set of exceptions to interpret as "Skip the whole module" during
-    # collection
-    skip_exceptions = (Skipped,)
-
     class CollectError(Exception):
         """ an error during collection, contains a custom message. """
 

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/nose.py
--- a/_pytest/nose.py
+++ b/_pytest/nose.py
@@ -1,17 +1,27 @@
 """ run test suites written for nose. """
 
-import pytest, py
 import sys
+
+import py
+import pytest
 from _pytest import unittest
 
+
+def get_skip_exceptions():
+    skip_classes = set()
+    for module_name in ('unittest', 'unittest2', 'nose'):
+        mod = sys.modules.get(module_name)
+        if hasattr(mod, 'SkipTest'):
+            skip_classes.add(mod.SkipTest)
+    return tuple(skip_classes)
+
+
 def pytest_runtest_makereport(__multicall__, item, call):
-    SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
-    if SkipTest:
-        if call.excinfo and call.excinfo.errisinstance(SkipTest):
-            # let's substitute the excinfo with a pytest.skip one
-            call2 = call.__class__(lambda:
-                        pytest.skip(str(call.excinfo.value)), call.when)
-            call.excinfo = call2.excinfo
+    if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()):
+        # let's substitute the excinfo with a pytest.skip one
+        call2 = call.__class__(lambda:
+                    pytest.skip(str(call.excinfo.value)), call.when)
+        call.excinfo = call2.excinfo
 
 
 @pytest.mark.trylast
@@ -38,13 +48,8 @@
         #    #call_optional(item._nosegensetup, 'teardown')
         #    del item.parent._nosegensetup
 
+
 def pytest_make_collect_report(collector):
-    SkipTest = getattr(sys.modules.get('unittest', None), 'SkipTest', None)
-    if SkipTest is not None:
-        collector.skip_exceptions += (SkipTest,)
-    SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
-    if SkipTest is not None:
-        collector.skip_exceptions += (SkipTest,)
     if isinstance(collector, pytest.Generator):
         call_optional(collector.obj, 'setup')
 

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -1841,7 +1841,13 @@
     if startindex is None:
         startindex = inspect.ismethod(function) and 1 or 0
     if realfunction != function:
-        startindex += len(getattr(function, "patchings", []))
+        mock = sys.modules.get('mock')
+        if mock is not None:
+            for patching in getattr(function, "patchings", []):
+                if not patching.attribute_name and patching.new is mock.DEFAULT:
+                    startindex += 1
+        else:
+            startindex += len(getattr(function, "patchings", []))
         function = realfunction
     argnames = inspect.getargs(py.code.getrawcode(function))[0]
     defaults = getattr(function, 'func_defaults',

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/runner.py
--- a/_pytest/runner.py
+++ b/_pytest/runner.py
@@ -274,7 +274,9 @@
     if not call.excinfo:
         outcome = "passed"
     else:
-        if call.excinfo.errisinstance(collector.skip_exceptions):
+        from _pytest import nose
+        skip_exceptions = (Skipped,) + nose.get_skip_exceptions()
+        if call.excinfo.errisinstance(skip_exceptions):
             outcome = "skipped"
             r = collector._repr_failure_py(call.excinfo, "line").reprcrash
             longrepr = (str(r.path), r.lineno, r.message)

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/_templates/links.html
--- a/doc/en/_templates/links.html
+++ b/doc/en/_templates/links.html
@@ -4,7 +4,7 @@
   <li><a href="{{ pathto('contributing') }}">Contribution Guide</a></li><li><a href="https://pypi.python.org/pypi/pytest">pytest @ PyPI</a></li><li><a href="https://bitbucket.org/hpk42/pytest/">pytest @ Bitbucket</a></li>
-  <li><a href="http://pytest.org/latest/plugins_index/index.html">3rd party plugins (beta)</a></li>
+  <li><a href="http://pytest.org/latest/plugins_index/index.html">3rd party plugins</a></li><li><a href="https://bitbucket.org/hpk42/pytest/issues?status=new&status=open">Issue Tracker</a></li><li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a></ul>

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/goodpractises.txt
--- a/doc/en/goodpractises.txt
+++ b/doc/en/goodpractises.txt
@@ -1,4 +1,3 @@
-
 .. highlightlang:: python
 .. _`goodpractises`:
 
@@ -69,7 +68,7 @@
 
 - **avoid "__init__.py" files in your test directories**.
   This way your tests can run easily against an installed version
-  of ``mypkg``, independently from if the installed package contains
+  of ``mypkg``, independently from the installed package if it contains
   the tests or not.
 
 - With inlined tests you might put ``__init__.py`` into test
@@ -190,12 +189,16 @@
         user_options = []
         def initialize_options(self):
             pass
+
         def finalize_options(self):
             pass
+
         def run(self):
             import sys,subprocess
             errno = subprocess.call([sys.executable, 'runtests.py'])
             raise SystemExit(errno)
+
+
     setup(
         #...,
         cmdclass = {'test': PyTest},
@@ -220,20 +223,24 @@
 Most often it is better to use tox_ instead, but here is how you can
 get started with setuptools integration::
 
+    import sys
+
     from setuptools.command.test import test as TestCommand
-    import sys
+
 
     class PyTest(TestCommand):
         def finalize_options(self):
             TestCommand.finalize_options(self)
             self.test_args = []
             self.test_suite = True
+
         def run_tests(self):
             #import here, cause outside the eggs aren't loaded
             import pytest
             errno = pytest.main(self.test_args)
             sys.exit(errno)
 
+
     setup(
         #...,
         tests_require=['pytest'],

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins.txt
--- a/doc/en/plugins.txt
+++ b/doc/en/plugins.txt
@@ -64,9 +64,10 @@
     pip uninstall pytest-NAME
 
 If a plugin is installed, ``pytest`` automatically finds and integrates it,
-there is no need to activate it.  We have a :doc:`beta page listing
-all 3rd party plugins and their status <plugins_index/index>` and here
-is a little annotated list for some popular plugins:
+there is no need to activate it.  We have a :doc:`page listing
+all 3rd party plugins and their status against the latest py.test version
+<plugins_index/index>` and here is a little annotated list
+for some popular plugins:
 
 .. _`django`: https://www.djangoproject.com/
 
@@ -109,7 +110,11 @@
 * `oejskit <http://pypi.python.org/pypi/oejskit>`_:
   a plugin to run javascript unittests in life browsers
 
-You may discover more plugins through a `pytest- pypi.python.org search`_.
+To see a complete list of all plugins with their latest testing
+status against different py.test and Python versions, please visit
+`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_.
+
+You may also discover more plugins through a `pytest- pypi.python.org search`_.
 
 .. _`available installable plugins`:
 .. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
@@ -304,6 +309,7 @@
 
 .. currentmodule:: _pytest.hookspec
 
+.. autofunction:: pytest_load_initial_conftests
 .. autofunction:: pytest_cmdline_preparse
 .. autofunction:: pytest_cmdline_parse
 .. autofunction:: pytest_namespace
@@ -315,7 +321,7 @@
 Generic "runtest" hooks
 ------------------------------
 
-All all runtest related hooks receive a :py:class:`pytest.Item` object.
+All runtest related hooks receive a :py:class:`pytest.Item` object.
 
 .. autofunction:: pytest_runtest_protocol
 .. autofunction:: pytest_runtest_setup

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/bitbucket.png
Binary file doc/en/plugins_index/bitbucket.png has changed

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/github.png
Binary file doc/en/plugins_index/github.png has changed

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/index.txt
--- a/doc/en/plugins_index/index.txt
+++ b/doc/en/plugins_index/index.txt
@@ -3,114 +3,125 @@
 List of Third-Party Plugins
 ===========================
 
-================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= =============================================================================================================================================
-                                       Name                                                                                           Py27                                                                                                        Py33                                                                              Repository                                                                                              Summary                                                                   
-================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= =============================================================================================================================================
-              `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2                   https://github.com/olegpidsadnyi/pytest-bdd                                                                         BDD for pytest                                                                
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                    
-     `pytest-bdd-splinter <http://pypi.python.org/pypi/pytest-bdd-splinter>`_          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2         https://github.com/olegpidsadnyi/pytest-bdd-splinter                                                        Splinter subplugin for Pytest BDD plugin                                                   
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2           :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                               
-            `pytest-bench <http://pypi.python.org/pypi/pytest-bench>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2                 http://github.com/concordusapps/pytest-bench                                                            Benchmark utility that plugs into pytest.                                                  
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-         `pytest-blockage <http://pypi.python.org/pypi/pytest-blockage>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2                 https://github.com/rob-b/pytest-blockage                                                             Disable network requests during a test run.                                                 
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
- `pytest-browsermob-proxy <http://pypi.python.org/pypi/pytest-browsermob-proxy>`_    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2     .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2        https://github.com/davehunt/pytest-browsermob-proxy                                                          BrowserMob proxy plugin for py.test.                                                     
-                                                                                        :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2       :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                             
-         `pytest-bugzilla <http://pypi.python.org/pypi/pytest-bugzilla>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2                http://github.com/nibrahim/pytest_bugzilla                                                                py.test bugzilla integration plugin                                                     
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-            `pytest-cache <http://pypi.python.org/pypi/pytest-cache>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2                   http://bitbucket.org/hpk42/pytest-cache/                                                     pytest plugin with mechanisms for caching across test runs                                          
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-       `pytest-capturelog <http://pypi.python.org/pypi/pytest-capturelog>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2        http://bitbucket.org/memedough/pytest-capturelog/overview                                                      py.test plugin to capture log messages                                                    
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-     `pytest-codecheckers <http://pypi.python.org/pypi/pytest-codecheckers>`_          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2      http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/                                        pytest plugin to add source code sanity checks (pep8 and friends)                                      
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2           :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                               
-   `pytest-contextfixture <http://pypi.python.org/pypi/pytest-contextfixture>`_       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2           http://github.com/pelme/pytest-contextfixture/                                                          Define pytest fixtures as context managers.                                                 
-                                                                                         :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2         :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                              
-       `pytest-couchdbkit <http://pypi.python.org/pypi/pytest-couchdbkit>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2        http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit                                          py.test extension for per-test couchdb databases using couchdbkit                                      
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-              `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2               http://bitbucket.org/memedough/pytest-cov/overview        py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing 
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                    
-       `pytest-dbfixtures <http://pypi.python.org/pypi/pytest-dbfixtures>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2             https://github.com/clearcode/pytest-dbfixtures                                                                dbfixtures plugin for py.test.                                                        
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-           `pytest-django <http://pypi.python.org/pypi/pytest-django>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2                    http://pytest-django.readthedocs.org/                                                                     A Django plugin for py.test.                                                         
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-      `pytest-django-lite <http://pypi.python.org/pypi/pytest-django-lite>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2              https://github.com/dcramer/pytest-django-lite                                                      The bare minimum to integrate py.test with Django.                                              
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-          `pytest-figleaf <http://pypi.python.org/pypi/pytest-figleaf>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2                  http://bitbucket.org/hpk42/pytest-figleaf                                                                  py.test figleaf coverage plugin                                                       
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-           `pytest-flakes <http://pypi.python.org/pypi/pytest-flakes>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2                  https://github.com/fschulze/pytest-flakes                                                         pytest plugin to check source code with pyflakes                                               
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-        `pytest-greendots <http://pypi.python.org/pypi/pytest-greendots>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2                                  UNKNOWN                                                                                         Green progress dots                                                             
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-            `pytest-growl <http://pypi.python.org/pypi/pytest-growl>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2                                    UNKNOWN                                                                               Growl notifications for pytest results.                                                   
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-        `pytest-httpretty <http://pypi.python.org/pypi/pytest-httpretty>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2                http://github.com/papaeye/pytest-httpretty                                                              A thin wrapper of HTTPretty for pytest                                                    
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-      `pytest-incremental <http://pypi.python.org/pypi/pytest-incremental>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2          https://bitbucket.org/schettino72/pytest-incremental                                                       an incremental test runner (pytest plugin)                                                  
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-        `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2               https://github.com/jpvanhal/pytest-instafail                                                            py.test plugin to show failures instantly                                                  
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-             `pytest-ipdb <http://pypi.python.org/pypi/pytest-ipdb>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2                   https://github.com/mverteuil/pytest-ipdb                                                 A py.test plug-in to enable drop to ipdb debugger on test failure.                                      
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-             `pytest-jira <http://pypi.python.org/pypi/pytest-jira>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2                     http://github.com/jlaska/pytest_jira                                                             py.test JIRA integration plugin, using markers                                                
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-           `pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2                 http://github.com/alfredodeza/pytest-konira                                                                Run Konira DSL tests with py.test                                                      
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-      `pytest-localserver <http://pypi.python.org/pypi/pytest-localserver>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2             http://bitbucket.org/basti/pytest-localserver/                                                      py.test plugin to test server connections locally.                                              
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-  `pytest-marker-bugzilla <http://pypi.python.org/pypi/pytest-marker-bugzilla>`_     .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2          http://github.com/eanxgeek/pytest_marker_bugzilla                                                    py.test bugzilla integration plugin, using markers                                              
-                                                                                        :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2        :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                              
-   `pytest-markfiltration <http://pypi.python.org/pypi/pytest-markfiltration>`_       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2        https://github.com/adamgoucher/pytest-markfiltration                                                                         UNKNOWN                                                                   
-                                                                                         :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2         :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                              
-            `pytest-marks <http://pypi.python.org/pypi/pytest-marks>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2                  https://github.com/adamgoucher/pytest-marks                                                                             UNKNOWN                                                                   
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-       `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2              http://bitbucket.org/hsoft/pytest-monkeyplus/                                                   pytest's monkeypatch subclass with extra functionalities                                           
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-         `pytest-mozwebqa <http://pypi.python.org/pypi/pytest-mozwebqa>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2                https://github.com/davehunt/pytest-mozwebqa                                                                Mozilla WebQA plugin for py.test.                                                      
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-             `pytest-oerp <http://pypi.python.org/pypi/pytest-oerp>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2                   http://github.com/santagada/pytest-oerp/                                                                pytest plugin to test OpenERP modules                                                    
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-        `pytest-osxnotify <http://pypi.python.org/pypi/pytest-osxnotify>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2                https://github.com/dbader/pytest-osxnotify                                                              OS X notifications for py.test results.                                                   
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-     `pytest-paste-config <http://pypi.python.org/pypi/pytest-paste-config>`_          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2                                UNKNOWN                                                                            Allow setting the path to a paste config file                                                
-                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2           :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                               
-             `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2                    http://bitbucket.org/hpk42/pytest-pep8/                                                              pytest plugin to check PEP8 requirements                                                   
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-              `pytest-poo <http://pypi.python.org/pypi/pytest-poo>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2                       http://github.com/pelme/pytest-poo                                                                        Visualize your crappy tests                                                         
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                    
-            `pytest-pydev <http://pypi.python.org/pypi/pytest-pydev>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2                   http://bitbucket.org/basti/pytest-pydev/                                              py.test plugin to connect to a remote debug server with PyDev or PyCharm.                                  
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-       `pytest-pythonpath <http://pypi.python.org/pypi/pytest-pythonpath>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2              https://github.com/bigsassy/pytest-pythonpath                                           pytest plugin for adding to the PYTHONPATH from command line or configs.                                   
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-               `pytest-qt <http://pypi.python.org/pypi/pytest-qt>`_                         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2                     http://github.com/nicoddemus/pytest-qt                                            pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications.                               
-                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2                     :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                    
-       `pytest-quickcheck <http://pypi.python.org/pypi/pytest-quickcheck>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2               http://bitbucket.org/t2y/pytest-quickcheck/                                                  pytest plugin to generate random data inspired by QuickCheck                                         
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                
-             `pytest-rage <http://pypi.python.org/pypi/pytest-rage>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2                   http://github.com/santagada/pytest-rage/                                                                  pytest plugin to implement PEP712                                                      
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-           `pytest-random <http://pypi.python.org/pypi/pytest-random>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2                   https://github.com/klrmn/pytest-random                                                                   py.test plugin to randomize tests                                                      
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-    `pytest-rerunfailures <http://pypi.python.org/pypi/pytest-rerunfailures>`_        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2             https://github.com/klrmn/pytest-rerunfailures                                                  py.test plugin to re-run tests to eliminate flakey failures                                         
-                                                                                         :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2          :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                               
-        `pytest-runfailed <http://pypi.python.org/pypi/pytest-runfailed>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2              http://github.com/dmerejkowsky/pytest-runfailed                                                           implement a --failed option for pytest                                                    
-                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-           `pytest-runner <http://pypi.python.org/pypi/pytest-runner>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2                 https://bitbucket.org/jaraco/pytest-runner                                                                              UNKNOWN                                                                   
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-            `pytest-sugar <http://pypi.python.org/pypi/pytest-sugar>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2                     http://pivotfinland.com/pytest-sugar/                 py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly).    
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-          `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2                  http://bitbucket.org/flub/pytest-timeout/                                                           pytest plugin to abort tests after a timeout                                                 
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-          `pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2                  https://github.com/schmir/pytest-twisted                                                                    A twisted plugin for py.test.                                                        
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                  
-            `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2                    http://bitbucket.org/hpk42/pytest-xdist                                               py.test xdist plugin for distributed testing and loop-on-failing modes                                    
-                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                   
-         `pytest-xprocess <http://pypi.python.org/pypi/pytest-xprocess>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2                http://bitbucket.org/hpk42/pytest-xprocess/                                                   pytest plugin to manage external processes across test runs                                         
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-         `pytest-yamlwsgi <http://pypi.python.org/pypi/pytest-yamlwsgi>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2                                  UNKNOWN                                                                             Run tests against wsgi apps defined in yaml                                                 
-                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                 
-              `pytest-zap <http://pypi.python.org/pypi/pytest-zap>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2                     https://github.com/davehunt/pytest-zap                                                                     OWASP ZAP plugin for py.test.                                                        
-                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                    
+The table below contains a listing of plugins found in PyPI and
+their status when tested using py.test **2.5.2** and python 2.7 and
+3.3.
 
-================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= =============================================================================================================================================
+A complete listing can also be found at
+`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_, which contains tests
+status against other py.test releases.
 
-*(Updated on 2014-02-11)*
+
+==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
+                                        Name                                                                                             Py27                                                                                                         Py33                                                                                       Repo                                                                                                       Summary                                                                   
+==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
+               `pytest-bdd <http://pypi.python.org/pypi/pytest-bdd>`_                         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2                   .. image:: github.png                                                                                                           BDD for pytest                                                                
+                                                                                                 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2                     :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2                     :target: https://github.com/olegpidsadnyi/pytest-bdd                                                                                                                                                       
+      `pytest-bdd-splinter <http://pypi.python.org/pypi/pytest-bdd-splinter>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2          .. image:: github.png                                                                                                   Splinter subplugin for Pytest BDD plugin                                                   
+                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2            :target: https://github.com/olegpidsadnyi/pytest-bdd-splinter                                                                                                                                                   
+             `pytest-bench <http://pypi.python.org/pypi/pytest-bench>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2                 .. image:: github.png                                                                                               Benchmark utility that plugs into pytest.                                                  
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2                   :target: http://github.com/concordusapps/pytest-bench                                                                                                                                                       
+          `pytest-blockage <http://pypi.python.org/pypi/pytest-blockage>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2                  .. image:: github.png                                                                                            Disable network requests during a test run.                                                 
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2                    :target: https://github.com/rob-b/pytest-blockage                                                                                                                                                         
+  `pytest-browsermob-proxy <http://pypi.python.org/pypi/pytest-browsermob-proxy>`_     .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2         .. image:: github.png                                                                                                    BrowserMob proxy plugin for py.test.                                                     
+                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2        :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2           :target: https://github.com/davehunt/pytest-browsermob-proxy                                                                                                                                                   
+          `pytest-bugzilla <http://pypi.python.org/pypi/pytest-bugzilla>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2                 .. image:: github.png                                                                                                 py.test bugzilla integration plugin                                                     
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2                   :target: http://github.com/nibrahim/pytest_bugzilla                                                                                                                                                        
+             `pytest-cache <http://pypi.python.org/pypi/pytest-cache>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2                   .. image:: bitbucket.png                                                                                 pytest plugin with mechanisms for caching across test runs                                          
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2                     :target: http://bitbucket.org/hpk42/pytest-cache/                                                                                                                                                         
+        `pytest-capturelog <http://pypi.python.org/pypi/pytest-capturelog>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2         .. image:: bitbucket.png                                                                                                   py.test plugin to capture log messages                                                    
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2           :target: http://bitbucket.org/memedough/pytest-capturelog/overview                                                                                                                                                
+      `pytest-codecheckers <http://pypi.python.org/pypi/pytest-codecheckers>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2       .. image:: bitbucket.png                                                                                       pytest plugin to add source code sanity checks (pep8 and friends)                                      
+                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2         :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/                                                                                                                                               
+    `pytest-contextfixture <http://pypi.python.org/pypi/pytest-contextfixture>`_        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2            .. image:: github.png                                                                                               Define pytest fixtures as context managers.                                                 
+                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2          :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2              :target: http://github.com/pelme/pytest-contextfixture/                                                                                                                                                      
+        `pytest-couchdbkit <http://pypi.python.org/pypi/pytest-couchdbkit>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2         .. image:: bitbucket.png                                                                                      py.test extension for per-test couchdb databases using couchdbkit                                      
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2           :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit                                                                                                                                                 
+               `pytest-cov <http://pypi.python.org/pypi/pytest-cov>`_                         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2               .. image:: bitbucket.png                                              py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing 
+                                                                                                 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2                     :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2                 :target: http://bitbucket.org/memedough/pytest-cov/overview                                                                                                                                                    
+        `pytest-dbfixtures <http://pypi.python.org/pypi/pytest-dbfixtures>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2              .. image:: github.png                                                                                                     dbfixtures plugin for py.test.                                                        
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2                :target: https://github.com/clearcode/pytest-dbfixtures                                                                                                                                                      
+ `pytest-dbus-notification <http://pypi.python.org/pypi/pytest-dbus-notification>`_    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.5.2     .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py33&pytest=2.5.2      .. image:: github.png                                                                                                     D-BUS notifications for pytest results.                                                   
+                                                                                          :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.5.2       :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py33&pytest=2.5.2        :target: https://github.com/bmathieu33/pytest-dbus-notification                                                                                                                                                  
+            `pytest-django <http://pypi.python.org/pypi/pytest-django>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2                      `link <http://pytest-django.readthedocs.org/>`_                                                                      A Django plugin for py.test.                                                         
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                               
+       `pytest-django-lite <http://pypi.python.org/pypi/pytest-django-lite>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2              .. image:: github.png                                                                                          The bare minimum to integrate py.test with Django.                                              
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2                :target: https://github.com/dcramer/pytest-django-lite                                                                                                                                                      
+           `pytest-figleaf <http://pypi.python.org/pypi/pytest-figleaf>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2                  .. image:: bitbucket.png                                                                                               py.test figleaf coverage plugin                                                       
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2                    :target: http://bitbucket.org/hpk42/pytest-figleaf                                                                                                                                                        
+            `pytest-flakes <http://pypi.python.org/pypi/pytest-flakes>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2                   .. image:: github.png                                                                                         pytest plugin to check source code with pyflakes                                               
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2                     :target: https://github.com/fschulze/pytest-flakes                                                                                                                                                        
+         `pytest-greendots <http://pypi.python.org/pypi/pytest-greendots>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2                                           ?                                                                                                  Green progress dots                                                             
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                             
+             `pytest-growl <http://pypi.python.org/pypi/pytest-growl>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2                                             ?                                                                                        Growl notifications for pytest results.                                                   
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                               
+         `pytest-httpretty <http://pypi.python.org/pypi/pytest-httpretty>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2                .. image:: github.png                                                                                               A thin wrapper of HTTPretty for pytest                                                    
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2                  :target: http://github.com/papaeye/pytest-httpretty                                                                                                                                                        
+       `pytest-incremental <http://pypi.python.org/pypi/pytest-incremental>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2          .. image:: bitbucket.png                                                                                               an incremental test runner (pytest plugin)                                                  
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2            :target: https://bitbucket.org/schettino72/pytest-incremental                                                                                                                                                   
+         `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2               .. image:: github.png                                                                                               py.test plugin to show failures instantly                                                  
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2                 :target: https://github.com/jpvanhal/pytest-instafail                                                                                                                                                       
+              `pytest-ipdb <http://pypi.python.org/pypi/pytest-ipdb>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2                    .. image:: github.png                                                                                A py.test plug-in to enable drop to ipdb debugger on test failure.                                      
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2                      :target: https://github.com/mverteuil/pytest-ipdb                                                                                                                                                         
+              `pytest-jira <http://pypi.python.org/pypi/pytest-jira>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2                      .. image:: github.png                                                                                        py.test JIRA integration plugin, using markers                                                
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2                        :target: http://github.com/jlaska/pytest_jira                                                                                                                                                           
+            `pytest-konira <http://pypi.python.org/pypi/pytest-konira>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2                  .. image:: github.png                                                                                                  Run Konira DSL tests with py.test                                                      
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2                    :target: http://github.com/alfredodeza/pytest-konira                                                                                                                                                       
+       `pytest-localserver <http://pypi.python.org/pypi/pytest-localserver>`_             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2             .. image:: bitbucket.png                                                                                        py.test plugin to test server connections locally.                                              
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2             :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2               :target: http://bitbucket.org/basti/pytest-localserver/                                                                                                                                                      
+   `pytest-marker-bugzilla <http://pypi.python.org/pypi/pytest-marker-bugzilla>`_       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2          .. image:: github.png                                                                                            py.test bugzilla integration plugin, using markers                                              
+                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2         :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2            :target: http://github.com/eanxgeek/pytest_marker_bugzilla                                                                                                                                                    
+    `pytest-markfiltration <http://pypi.python.org/pypi/pytest-markfiltration>`_        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2        .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2         .. image:: github.png                                                                                                                    UNKNOWN                                                                   
+                                                                                           :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2          :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2           :target: https://github.com/adamgoucher/pytest-markfiltration                                                                                                                                                   
+             `pytest-marks <http://pypi.python.org/pypi/pytest-marks>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2                  .. image:: github.png                                                                                                               UNKNOWN                                                                   
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2                    :target: https://github.com/adamgoucher/pytest-marks                                                                                                                                                       
+        `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2               .. image:: bitbucket.png                                                                                    pytest's monkeypatch subclass with extra functionalities                                           
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2                 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/                                                                                                                                                      
+          `pytest-mozwebqa <http://pypi.python.org/pypi/pytest-mozwebqa>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2                 .. image:: github.png                                                                                                  Mozilla WebQA plugin for py.test.                                                      
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2                   :target: https://github.com/davehunt/pytest-mozwebqa                                                                                                                                                       
+              `pytest-oerp <http://pypi.python.org/pypi/pytest-oerp>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2                    .. image:: github.png                                                                                               pytest plugin to test OpenERP modules                                                    
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2                      :target: http://github.com/santagada/pytest-oerp/                                                                                                                                                         
+         `pytest-osxnotify <http://pypi.python.org/pypi/pytest-osxnotify>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2                .. image:: github.png                                                                                               OS X notifications for py.test results.                                                   
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2                  :target: https://github.com/dbader/pytest-osxnotify                                                                                                                                                        
+      `pytest-paste-config <http://pypi.python.org/pypi/pytest-paste-config>`_           .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2                                          ?                                                                                     Allow setting the path to a paste config file                                                
+                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2            :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                            
+              `pytest-pep8 <http://pypi.python.org/pypi/pytest-pep8>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2                     .. image:: bitbucket.png                                                                                         pytest plugin to check PEP8 requirements                                                   
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2                       :target: http://bitbucket.org/hpk42/pytest-pep8/                                                                                                                                                         
+               `pytest-poo <http://pypi.python.org/pypi/pytest-poo>`_                         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2                       .. image:: github.png                                                                                                 Visualize your crappy tests                                                         
+                                                                                                 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2                     :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2                         :target: http://github.com/pelme/pytest-poo                                                                                                                                                            
+             `pytest-pydev <http://pypi.python.org/pypi/pytest-pydev>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2                   .. image:: bitbucket.png                                                                          py.test plugin to connect to a remote debug server with PyDev or PyCharm.                                  
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2                     :target: http://bitbucket.org/basti/pytest-pydev/                                                                                                                                                         
+        `pytest-pythonpath <http://pypi.python.org/pypi/pytest-pythonpath>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2               .. image:: github.png                                                                               pytest plugin for adding to the PYTHONPATH from command line or configs.                                   
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2                 :target: https://github.com/bigsassy/pytest-pythonpath                                                                                                                                                      
+                `pytest-qt <http://pypi.python.org/pypi/pytest-qt>`_                          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2                      .. image:: github.png                                                                         pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications.                               
+                                                                                                 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2                      :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2                        :target: http://github.com/nicoddemus/pytest-qt                                                                                                                                                          
+        `pytest-quickcheck <http://pypi.python.org/pypi/pytest-quickcheck>`_              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2            .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2                .. image:: bitbucket.png                                                                                 pytest plugin to generate random data inspired by QuickCheck                                         
+                                                                                             :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2              :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2                  :target: http://bitbucket.org/t2y/pytest-quickcheck/                                                                                                                                                       
+              `pytest-rage <http://pypi.python.org/pypi/pytest-rage>`_                       .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2                  .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2                    .. image:: github.png                                                                                                 pytest plugin to implement PEP712                                                      
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2                    :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2                      :target: http://github.com/santagada/pytest-rage/                                                                                                                                                         
+            `pytest-random <http://pypi.python.org/pypi/pytest-random>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2                    .. image:: github.png                                                                                                py.test plugin to randomize tests                                                      
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2                      :target: https://github.com/klrmn/pytest-random                                                                                                                                                          
+     `pytest-rerunfailures <http://pypi.python.org/pypi/pytest-rerunfailures>`_          .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2             .. image:: github.png                                                                                      py.test plugin to re-run tests to eliminate flakey failures                                         
+                                                                                            :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2           :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2               :target: https://github.com/klrmn/pytest-rerunfailures                                                                                                                                                      
+         `pytest-runfailed <http://pypi.python.org/pypi/pytest-runfailed>`_                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2             .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2              .. image:: github.png                                                                                                 implement a --failed option for pytest                                                    
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2               :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2                :target: http://github.com/dmerejkowsky/pytest-runfailed                                                                                                                                                     
+            `pytest-runner <http://pypi.python.org/pypi/pytest-runner>`_                    .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2                .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2                  .. image:: bitbucket.png                                                                                                            UNKNOWN                                                                   
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2                  :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2                    :target: https://bitbucket.org/jaraco/pytest-runner                                                                                                                                                        
+             `pytest-sugar <http://pypi.python.org/pypi/pytest-sugar>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2                      `link <http://pivotfinland.com/pytest-sugar/>`_                  py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly).    
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                               
+           `pytest-timeout <http://pypi.python.org/pypi/pytest-timeout>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2                  .. image:: bitbucket.png                                                                                        pytest plugin to abort tests after a timeout                                                 
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2                    :target: http://bitbucket.org/flub/pytest-timeout/                                                                                                                                                        
+           `pytest-twisted <http://pypi.python.org/pypi/pytest-twisted>`_                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2               .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2                  .. image:: github.png                                                                                                   A twisted plugin for py.test.                                                        
+                                                                                               :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2                 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2                    :target: https://github.com/schmir/pytest-twisted                                                                                                                                                         
+             `pytest-xdist <http://pypi.python.org/pypi/pytest-xdist>`_                      .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2                    .. image:: bitbucket.png                                                                          py.test xdist plugin for distributed testing and loop-on-failing modes                                    
+                                                                                                :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2                   :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2                      :target: http://bitbucket.org/hpk42/pytest-xdist                                                                                                                                                         
+          `pytest-xprocess <http://pypi.python.org/pypi/pytest-xprocess>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2                 .. image:: bitbucket.png                                                                                  pytest plugin to manage external processes across test runs                                         
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2                   :target: http://bitbucket.org/hpk42/pytest-xprocess/                                                                                                                                                       
+          `pytest-yamlwsgi <http://pypi.python.org/pypi/pytest-yamlwsgi>`_                 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2              .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2                                            ?                                                                                      Run tests against wsgi apps defined in yaml                                                 
+                                                                                              :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2                :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2                                                                                                                                                                                                                              
+               `pytest-zap <http://pypi.python.org/pypi/pytest-zap>`_                         .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2                   .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2                     .. image:: github.png                                                                                                  OWASP ZAP plugin for py.test.                                                        
+                                                                                                 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2                     :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2                       :target: https://github.com/davehunt/pytest-zap                                                                                                                                                          
+
+==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= =============================================================================================================================================
+
+*(Updated on 2014-02-18)*

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/plugins_index.py
--- a/doc/en/plugins_index/plugins_index.py
+++ b/doc/en/plugins_index/plugins_index.py
@@ -1,6 +1,13 @@
 """
 Script to generate the file `index.txt` with information about
-pytest plugins taken directly from a live PyPI server.
+pytest plugins taken directly from PyPI.
+
+Usage:
+    python plugins_index.py
+
+This command will update `index.txt` in the same directory found as this script.
+This should be issued before every major documentation release to obtain latest
+versions from PyPI.
 
 Also includes plugin compatibility between different python and pytest versions,
 obtained from http://pytest-plugs.herokuapp.com.
@@ -66,9 +73,34 @@
     :param plugins: list of (name, version)
     :param client: ServerProxy
     """
+    def get_repo_markup(repo):
+        """
+        obtains appropriate markup for the given repository, as two lines
+        that should be output in the same table row. We use this to display an icon
+        for known repository hosts (github, etc), just a "?" char when
+        repository is not registered in pypi or a simple link otherwise.
+        """
+        target = repo
+        if 'github.com' in repo:
+            image = 'github.png'
+        elif 'bitbucket.org' in repo:
+            image = 'bitbucket.png'
+        elif repo.lower() == 'unknown':
+            return '?', ''
+        else:
+            image = None
+
+        if image is not None:
+            image_markup = '.. image:: %s' % image
+            target_markup = '   :target: %s' % repo
+            pad_right = ('%-' + str(len(target_markup)) + 's')
+            return pad_right % image_markup, target_markup
+        else:
+            return '`link <%s>`_' % target, ''
+
     rows = []
     ColumnData = namedtuple('ColumnData', 'text link')
-    headers = ['Name', 'Py27', 'Py33', 'Repository', 'Summary']
+    headers = ['Name', 'Py27', 'Py33', 'Repo', 'Summary']
     pytest_version = pytest.__version__
     repositories = obtain_override_repositories()
     print('*** pytest-{0} ***'.format(pytest_version))
@@ -83,6 +115,9 @@
             name=package_name,
             version=version)
 
+        repository = repositories.get(package_name, release_data['home_page'])
+        repo_markup_1, repo_markup_2 = get_repo_markup(repository)
+
         # first row: name, images and simple links
         url = '.. image:: {site}/status/{name}-latest'
         image_url = url.format(**common_params)
@@ -94,7 +129,7 @@
             ColumnData(image_url.format(py='py33', pytest=pytest_version),
                        None),
             ColumnData(
-                repositories.get(package_name, release_data['home_page']),
+                repo_markup_1,
                 None),
             ColumnData(release_data['summary'], None),
         )
@@ -112,8 +147,9 @@
                        None),
             ColumnData(output_url.format(py='py33', pytest=pytest_version),
                        None),
+            ColumnData(repo_markup_2, None),
             ColumnData('', None),
-            ColumnData('', None),
+
         )
         assert len(row) == len(headers)
         rows.append(row)
@@ -167,11 +203,9 @@
         return ' '.join(char * length for length in column_lengths)
 
     with open(filename, 'w') as f:
-        # write welcome
-        print('.. _plugins_index:', file=f)
-        print(file=f)
-        print('List of Third-Party Plugins', file=f)
-        print('===========================', file=f)
+        # header
+        header_text = HEADER.format(pytest_version=pytest.__version__)
+        print(header_text, file=f)
         print(file=f)
 
         # table
@@ -232,5 +266,21 @@
     return 0
 
 
+# header for the plugins_index page
+HEADER = '''.. _plugins_index:
+
+List of Third-Party Plugins
+===========================
+
+The table below contains a listing of plugins found in PyPI and
+their status when tested using py.test **{pytest_version}** and python 2.7 and
+3.3.
+
+A complete listing can also be found at
+`pytest-plugs <http://pytest-plugs.herokuapp.com/>`_, which contains tests
+status against other py.test releases.
+'''
+
+
 if __name__ == '__main__':
     sys.exit(main(sys.argv))

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/skipping.txt
--- a/doc/en/skipping.txt
+++ b/doc/en/skipping.txt
@@ -286,4 +286,9 @@
     def test_function(...):
         pass
 
+.. note::
 
+    You cannot use ``pytest.config.getvalue()`` in code
+    imported before py.test's argument parsing takes place.  For example,
+    ``conftest.py`` files are imported before command line parsing and thus
+    ``config.getvalue()`` will not execute correctly.

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -277,7 +277,7 @@
         assert hasattr(modcol.obj, 'test_func')
 
     def test_function_as_object_instance_ignored(self, testdir):
-        item = testdir.makepyfile("""
+        testdir.makepyfile("""
             class A:
                 def __call__(self, tmpdir):
                     0/0

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/python/integration.py
--- a/testing/python/integration.py
+++ b/testing/python/integration.py
@@ -124,12 +124,16 @@
                 def test_hello(self, abspath):
                     os.path.abspath("hello")
                     abspath.assert_any_call("hello")
+            def mock_basename(path):
+                return "mock_basename"
             @mock.patch("os.path.abspath")
             @mock.patch("os.path.normpath")
+            @mock.patch("os.path.basename", new=mock_basename)
             def test_someting(normpath, abspath, tmpdir):
                 abspath.return_value = "this"
                 os.path.normpath(os.path.abspath("hello"))
                 normpath.assert_any_call("this")
+                assert os.path.basename("123") == "mock_basename"
         """)
         reprec = testdir.inline_run()
         reprec.assertoutcome(passed=2)

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -533,6 +533,24 @@
     assert 'hello19' not in result.stdout.str()
 
 
+ at pytest.mark.xfail(reason='demonstrate #412')
+def test_capture_badoutput(testdir):
+    testdir.makepyfile("""
+        import os
+
+        def test_func():
+            omg = bytearray([1,129,1])
+            os.write(1, omg)
+            assert 0
+        """)
+    result = testdir.runpytest('--cap=fd')
+    #this fails on  python3 - fnmatch first for debugging
+    result.stdout.fnmatch_lines([
+        '*1 failed*',
+        ])
+    assert result.ret == 1
+
+
 def test_capture_early_option_parsing(testdir):
     testdir.makeconftest("""
         def pytest_runtest_setup():

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/test_nose.py
--- a/testing/test_nose.py
+++ b/testing/test_nose.py
@@ -330,12 +330,26 @@
     reprec = testdir.inline_run()
     reprec.assertoutcome(passed=1, skipped=1)
 
+
 def test_SkipTest_during_collection(testdir):
-    testdir.makepyfile("""
+    p = testdir.makepyfile("""
         import nose
         raise nose.SkipTest("during collection")
         def test_failing():
             assert False
         """)
+    result = testdir.runpytest(p)
+    outcome = result.parseoutcomes()
+    outcome.pop('seconds')
+    assert outcome == dict(skipped=1)
+
+
+def test_SkipTest_in_test(testdir):
+    testdir.makepyfile("""
+        import nose
+
+        def test_skipping():
+            raise nose.SkipTest("in test")
+        """)
     reprec = testdir.inline_run()
     reprec.assertoutcome(skipped=1)

diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -7,6 +7,7 @@
 commands= py.test --lsof -rfsxX --junitxml={envlogdir}/junit-{envname}.xml []
 deps=
     nose
+    mock
 
 [testenv:genscript]
 changedir=.


https://bitbucket.org/hpk42/pytest/commits/b7aa02ac2440/
Changeset:   b7aa02ac2440
User:        hpk42
Date:        2014-03-28 07:03:34
Summary:     remove unused "suspend/resume" on capturing, some formatting cleanup
Affected #:  2 files

diff -r b36800a30a51911876ae23ff0fc187145e0f9f03 -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -426,7 +426,7 @@
         if hasattr(self, '_reset'):
             raise ValueError("was already reset")
         self._reset = True
-        outfile, errfile = self.stop_capturing(save=False)
+        outfile, errfile = self.stop_capturing()
         out, err = "", ""
         if outfile and not outfile.closed:
             out = outfile.read()
@@ -452,24 +452,9 @@
         is invalid it will not be captured.
     """
     def __init__(self, out=True, err=True, in_=True, patchsys=True):
-        self._options = {
-            "out": out,
-            "err": err,
-            "in_": in_,
-            "patchsys": patchsys,
-        }
-        self._save()
-
-    def _save(self):
-        in_ = self._options['in_']
-        out = self._options['out']
-        err = self._options['err']
-        patchsys = self._options['patchsys']
         if in_:
             try:
-                self.in_ = FDCapture(
-                    0, tmpfile=None,
-                    patchsys=patchsys)
+                self.in_ = FDCapture(0, tmpfile=None, patchsys=patchsys)
             except OSError:
                 pass
         if out:
@@ -477,10 +462,7 @@
             if hasattr(out, 'write'):
                 tmpfile = out
             try:
-                self.out = FDCapture(
-                    1, tmpfile=tmpfile,
-                    patchsys=patchsys)
-                self._options['out'] = self.out.tmpfile
+                self.out = FDCapture(1, tmpfile=tmpfile, patchsys=patchsys)
             except OSError:
                 pass
         if err:
@@ -489,10 +471,7 @@
             else:
                 tmpfile = None
             try:
-                self.err = FDCapture(
-                    2, tmpfile=tmpfile,
-                    patchsys=patchsys)
-                self._options['err'] = self.err.tmpfile
+                self.err = FDCapture(2, tmpfile=tmpfile, patchsys=patchsys)
             except OSError:
                 pass
 
@@ -507,7 +486,7 @@
     #def pytest_sessionfinish(self):
     #    self.reset_capturings()
 
-    def stop_capturing(self, save=True):
+    def stop_capturing(self):
         """ return (outfile, errfile) and stop capturing. """
         outfile = errfile = None
         if hasattr(self, 'out') and not self.out.tmpfile.closed:
@@ -516,8 +495,6 @@
             errfile = self.err.done()
         if hasattr(self, 'in_'):
             self.in_.done()
-        if save:
-            self._save()
         return outfile, errfile
 
     def readouterr(self):
@@ -577,7 +554,7 @@
         if self.in_:
             sys.stdin = self.in_ = DontReadFromInput()
 
-    def stop_capturing(self, save=True):
+    def stop_capturing(self):
         """ return (outfile, errfile) and stop capturing. """
         outfile = errfile = None
         if self.out and not self.out.closed:

diff -r b36800a30a51911876ae23ff0fc187145e0f9f03 -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -873,25 +873,6 @@
         pytest.raises(IOError, "sys.stdin.read()")
         out, err = cap.reset()
 
-    def test_suspend_resume(self):
-        cap = self.getcapture(out=True, err=False, in_=False)
-        try:
-            print ("hello")
-            sys.stderr.write("error\n")
-            out, err = cap.readouterr()
-            cap.stop_capturing()
-            assert out == "hello\n"
-            assert not err
-            print ("in between")
-            sys.stderr.write("in between\n")
-            cap.start_capturing()
-            print ("after")
-            sys.stderr.write("error_after\n")
-        finally:
-            out, err = cap.reset()
-        assert out == "after\n"
-        assert not err
-
 
 class TestStdCaptureFD(TestStdCapture):
     pytestmark = needsosdup
@@ -925,12 +906,9 @@
 @needsosdup
 def test_stdcapture_fd_tmpfile(tmpfile):
     capfd = capture.StdCaptureFD(out=tmpfile)
-    try:
-        os.write(1, "hello".encode("ascii"))
-        os.write(2, "world".encode("ascii"))
-        outf, errf = capfd.stop_capturing()
-    finally:
-        capfd.reset()
+    os.write(1, "hello".encode("ascii"))
+    os.write(2, "world".encode("ascii"))
+    outf, errf = capfd.stop_capturing()
     assert outf == tmpfile
 
 


https://bitbucket.org/hpk42/pytest/commits/080d7f3e486f/
Changeset:   080d7f3e486f
User:        hpk42
Date:        2014-03-28 07:03:37
Summary:     unify and normalize Sys/FD Capturing classes
* * *
more unification
Affected #:  2 files

diff -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 -r 080d7f3e486f387bb501369a0444e32313e59a7d _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -100,44 +100,25 @@
         raise
 
 
-class NoCapture:
-    def start_capturing(self):
-        pass
 
-    def stop_capturing(self):
-        pass
-
-    def pop_outerr_to_orig(self):
-        pass
-
-    def reset(self):
-        pass
-
-    def readouterr(self):
-        return "", ""
-
+def maketmpfile():
+    f = py.std.tempfile.TemporaryFile()
+    newf = dupfile(f, encoding="UTF-8")
+    f.close()
+    return newf
 
 class CaptureManager:
     def __init__(self, defaultmethod=None):
         self._method2capture = {}
         self._defaultmethod = defaultmethod
 
-    def _maketempfile(self):
-        f = py.std.tempfile.TemporaryFile()
-        newf = dupfile(f, encoding="UTF-8")
-        f.close()
-        return newf
-
     def _getcapture(self, method):
         if method == "fd":
-            return StdCaptureFD(
-                out=self._maketempfile(),
-                err=self._maketempfile(),
-            )
+            return StdCaptureBase(out=True, err=True, Capture=FDCapture)
         elif method == "sys":
-            return StdCapture(out=TextIO(), err=TextIO())
+            return StdCaptureBase(out=True, err=True, Capture=SysCapture)
         elif method == "no":
-            return NoCapture()
+            return StdCaptureBase(out=False, err=False, in_=False)
         else:
             raise ValueError("unknown capturing method: %r" % method)
 
@@ -277,8 +258,7 @@
     """
     if "capfd" in request._funcargs:
         raise request.raiseerror(error_capsysfderror)
-    return CaptureFixture(StdCapture)
-
+    return CaptureFixture(SysCapture)
 
 def pytest_funcarg__capfd(request):
     """enables capturing of writes to file descriptors 1 and 2 and makes
@@ -289,12 +269,13 @@
         request.raiseerror(error_capsysfderror)
     if not hasattr(os, 'dup'):
         pytest.skip("capfd funcarg needs os.dup")
-    return CaptureFixture(StdCaptureFD)
+    return CaptureFixture(FDCapture)
 
 
 class CaptureFixture:
     def __init__(self, captureclass):
-        self._capture = captureclass(in_=False)
+        self._capture = StdCaptureBase(out=True, err=True, in_=False,
+                                   Capture=captureclass)
 
     def _start(self):
         self._capture.start_capturing()
@@ -315,63 +296,6 @@
         self._finalize()
 
 
-class FDCapture:
-    """ Capture IO to/from a given os-level filedescriptor. """
-
-    def __init__(self, targetfd, tmpfile=None, patchsys=False):
-        """ save targetfd descriptor, and open a new
-            temporary file there.  If no tmpfile is
-            specified a tempfile.Tempfile() will be opened
-            in text mode.
-        """
-        self.targetfd = targetfd
-        if tmpfile is None and targetfd != 0:
-            # this code path is covered in the tests
-            # but not used by a regular pytest run
-            f = tempfile.TemporaryFile('wb+')
-            tmpfile = dupfile(f, encoding="UTF-8")
-            f.close()
-        self.tmpfile = tmpfile
-        self._savefd = os.dup(self.targetfd)
-        if patchsys:
-            self._oldsys = getattr(sys, patchsysdict[targetfd])
-
-    def start(self):
-        try:
-            os.fstat(self._savefd)
-        except OSError:
-            raise ValueError(
-                "saved filedescriptor not valid, "
-                "did you call start() twice?")
-        if self.targetfd == 0 and not self.tmpfile:
-            fd = os.open(os.devnull, os.O_RDONLY)
-            os.dup2(fd, 0)
-            os.close(fd)
-            if hasattr(self, '_oldsys'):
-                setattr(sys, patchsysdict[self.targetfd], DontReadFromInput())
-        else:
-            os.dup2(self.tmpfile.fileno(), self.targetfd)
-            if hasattr(self, '_oldsys'):
-                setattr(sys, patchsysdict[self.targetfd], self.tmpfile)
-
-    def done(self):
-        """ unpatch and clean up, returns the self.tmpfile (file object)
-        """
-        os.dup2(self._savefd, self.targetfd)
-        os.close(self._savefd)
-        if self.targetfd != 0:
-            self.tmpfile.seek(0)
-        if hasattr(self, '_oldsys'):
-            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
-        return self.tmpfile
-
-    def writeorg(self, data):
-        """ write a string to the original file descriptor
-        """
-        if py.builtin._istext(data):
-            data = data.encode("utf8") # XXX use encoding of original stream
-        os.write(self._savefd, data)
-
 
 def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
     """ return a new open file object that's a duplicate of f
@@ -421,6 +345,16 @@
 
 
 class StdCaptureBase(object):
+    out = err = in_ = None
+
+    def __init__(self, out=True, err=True, in_=True, Capture=None):
+        if in_:
+            self.in_ = Capture(0)
+        if out:
+            self.out = Capture(1)
+        if err:
+            self.err = Capture(2)
+
     def reset(self):
         """ reset sys.stdout/stderr and return captured output as strings. """
         if hasattr(self, '_reset'):
@@ -436,6 +370,25 @@
             errfile.close()
         return out, err
 
+    def start_capturing(self):
+        if self.in_:
+            self.in_.start()
+        if self.out:
+            self.out.start()
+        if self.err:
+            self.err.start()
+
+    def stop_capturing(self):
+        """ return (outfile, errfile) and stop capturing. """
+        outfile = errfile = None
+        if self.out:
+            outfile = self.out.done()
+        if self.err:
+            errfile = self.err.done()
+        if self.in_:
+            self.in_.done()
+        return outfile, errfile
+
     def pop_outerr_to_orig(self):
         """ pop current snapshot out/err capture and flush to orig streams. """
         out, err = self.readouterr()
@@ -444,61 +397,8 @@
         if err:
             self.err.writeorg(err)
 
-
-class StdCaptureFD(StdCaptureBase):
-    """ This class allows to capture writes to FD1 and FD2
-        and may connect a NULL file to FD0 (and prevent
-        reads from sys.stdin).  If any of the 0,1,2 file descriptors
-        is invalid it will not be captured.
-    """
-    def __init__(self, out=True, err=True, in_=True, patchsys=True):
-        if in_:
-            try:
-                self.in_ = FDCapture(0, tmpfile=None, patchsys=patchsys)
-            except OSError:
-                pass
-        if out:
-            tmpfile = None
-            if hasattr(out, 'write'):
-                tmpfile = out
-            try:
-                self.out = FDCapture(1, tmpfile=tmpfile, patchsys=patchsys)
-            except OSError:
-                pass
-        if err:
-            if hasattr(err, 'write'):
-                tmpfile = err
-            else:
-                tmpfile = None
-            try:
-                self.err = FDCapture(2, tmpfile=tmpfile, patchsys=patchsys)
-            except OSError:
-                pass
-
-    def start_capturing(self):
-        if hasattr(self, 'in_'):
-            self.in_.start()
-        if hasattr(self, 'out'):
-            self.out.start()
-        if hasattr(self, 'err'):
-            self.err.start()
-
-    #def pytest_sessionfinish(self):
-    #    self.reset_capturings()
-
-    def stop_capturing(self):
-        """ return (outfile, errfile) and stop capturing. """
-        outfile = errfile = None
-        if hasattr(self, 'out') and not self.out.tmpfile.closed:
-            outfile = self.out.done()
-        if hasattr(self, 'err') and not self.err.tmpfile.closed:
-            errfile = self.err.done()
-        if hasattr(self, 'in_'):
-            self.in_.done()
-        return outfile, errfile
-
     def readouterr(self):
-        """ return snapshot value of stdout/stderr capturings. """
+        """ return snapshot unicode value of stdout/stderr capturings. """
         return self._readsnapshot('out'), self._readsnapshot('err')
 
     def _readsnapshot(self, name):
@@ -511,77 +411,87 @@
         f.seek(0)
         res = f.read()
         enc = getattr(f, "encoding", None)
-        if enc:
+        if enc and isinstance(res, bytes):
             res = py.builtin._totext(res, enc, "replace")
         f.truncate(0)
         f.seek(0)
         return res
 
-class TextCapture(TextIO):
-    def __init__(self, oldout):
-        super(TextCapture, self).__init__()
-        self._oldout = oldout
+
+class FDCapture:
+    """ Capture IO to/from a given os-level filedescriptor. """
+
+    def __init__(self, targetfd, tmpfile=None):
+        self.targetfd = targetfd
+        try:
+            self._savefd = os.dup(self.targetfd)
+        except OSError:
+            self.start = lambda: None
+            self.done = lambda: None
+        else:
+            if tmpfile is None:
+                if targetfd == 0:
+                    tmpfile = open(os.devnull, "r")
+                else:
+                    tmpfile = maketmpfile()
+            self.tmpfile = tmpfile
+            if targetfd in patchsysdict:
+                self._oldsys = getattr(sys, patchsysdict[targetfd])
+
+    def start(self):
+        """ Start capturing on targetfd using memorized tmpfile. """
+        try:
+            os.fstat(self._savefd)
+        except OSError:
+            raise ValueError("saved filedescriptor not valid anymore")
+        targetfd = self.targetfd
+        os.dup2(self.tmpfile.fileno(), targetfd)
+        if hasattr(self, '_oldsys'):
+            subst = self.tmpfile if targetfd != 0 else DontReadFromInput()
+            setattr(sys, patchsysdict[targetfd], subst)
+
+    def done(self):
+        """ stop capturing, restore streams, return original capture file,
+        seeked to position zero. """
+        os.dup2(self._savefd, self.targetfd)
+        os.close(self._savefd)
+        if self.targetfd != 0:
+            self.tmpfile.seek(0)
+        if hasattr(self, '_oldsys'):
+            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
+        return self.tmpfile
 
     def writeorg(self, data):
-        self._oldout.write(data)
-        self._oldout.flush()
+        """ write a string to the original file descriptor
+        """
+        if py.builtin._istext(data):
+            data = data.encode("utf8") # XXX use encoding of original stream
+        os.write(self._savefd, data)
 
 
-class StdCapture(StdCaptureBase):
-    """ This class allows to capture writes to sys.stdout|stderr "in-memory"
-        and will raise errors on tries to read from sys.stdin. It only
-        modifies sys.stdout|stderr|stdin attributes and does not
-        touch underlying File Descriptors (use StdCaptureFD for that).
-    """
-    def __init__(self, out=True, err=True, in_=True):
-        self._oldout = sys.stdout
-        self._olderr = sys.stderr
-        self._oldin = sys.stdin
-        if out and not hasattr(out, 'file'):
-            out = TextCapture(self._oldout)
-        self.out = out
-        if err:
-            if not hasattr(err, 'write'):
-                err = TextCapture(self._olderr)
-        self.err = err
-        self.in_ = in_
+class SysCapture:
+    def __init__(self, fd):
+        name = patchsysdict[fd]
+        self._old = getattr(sys, name)
+        self.name = name
+        if name == "stdin":
+            self.tmpfile = DontReadFromInput()
+        else:
+            self.tmpfile = TextIO()
 
-    def start_capturing(self):
-        if self.out:
-            sys.stdout = self.out
-        if self.err:
-            sys.stderr = self.err
-        if self.in_:
-            sys.stdin = self.in_ = DontReadFromInput()
+    def start(self):
+        setattr(sys, self.name, self.tmpfile)
 
-    def stop_capturing(self):
-        """ return (outfile, errfile) and stop capturing. """
-        outfile = errfile = None
-        if self.out and not self.out.closed:
-            sys.stdout = self._oldout
-            outfile = self.out
-            outfile.seek(0)
-        if self.err and not self.err.closed:
-            sys.stderr = self._olderr
-            errfile = self.err
-            errfile.seek(0)
-        if self.in_:
-            sys.stdin = self._oldin
-        return outfile, errfile
+    def done(self):
+        setattr(sys, self.name, self._old)
+        if self.name != "stdin":
+            self.tmpfile.seek(0)
+        return self.tmpfile
 
+    def writeorg(self, data):
+        self._old.write(data)
+        self._old.flush()
 
-    def readouterr(self):
-        """ return snapshot value of stdout/stderr capturings. """
-        out = err = ""
-        if self.out:
-            out = self.out.getvalue()
-            self.out.truncate(0)
-            self.out.seek(0)
-        if self.err:
-            err = self.err.getvalue()
-            self.err.truncate(0)
-            self.err.seek(0)
-        return out, err
 
 
 class DontReadFromInput:

diff -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 -r 080d7f3e486f387bb501369a0444e32313e59a7d testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -4,6 +4,7 @@
 import os
 import sys
 import py
+import tempfile
 import pytest
 import contextlib
 
@@ -44,6 +45,13 @@
 
 
 
+def StdCaptureFD(out=True, err=True, in_=True):
+    return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture)
+
+def StdCapture(out=True, err=True, in_=True):
+    return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture)
+
+
 class TestCaptureManager:
     def test_getmethod_default_no_fd(self, testdir, monkeypatch):
         config = testdir.parseconfig(testdir.tmpdir)
@@ -75,7 +83,7 @@
     @needsosdup
     @pytest.mark.parametrize("method", ['no', 'fd', 'sys'])
     def test_capturing_basic_api(self, method):
-        capouter = capture.StdCaptureFD()
+        capouter = StdCaptureFD()
         old = sys.stdout, sys.stderr, sys.stdin
         try:
             capman = CaptureManager()
@@ -99,7 +107,7 @@
 
     @needsosdup
     def test_juggle_capturings(self, testdir):
-        capouter = capture.StdCaptureFD()
+        capouter = StdCaptureFD()
         try:
             #config = testdir.parseconfig(testdir.tmpdir)
             capman = CaptureManager()
@@ -717,7 +725,7 @@
         f.close()
 
     def test_stderr(self):
-        cap = capture.FDCapture(2, patchsys=True)
+        cap = capture.FDCapture(2)
         cap.start()
         print_("hello", file=sys.stderr)
         f = cap.done()
@@ -727,7 +735,7 @@
     def test_stdin(self, tmpfile):
         tmpfile.write(tobytes("3"))
         tmpfile.seek(0)
-        cap = capture.FDCapture(0, tmpfile=tmpfile)
+        cap = capture.FDCapture(0, tmpfile)
         cap.start()
         # check with os.read() directly instead of raw_input(), because
         # sys.stdin itself may be redirected (as pytest now does by default)
@@ -753,7 +761,7 @@
 
 class TestStdCapture:
     def getcapture(self, **kw):
-        cap = capture.StdCapture(**kw)
+        cap = StdCapture(**kw)
         cap.start_capturing()
         return cap
 
@@ -878,7 +886,7 @@
     pytestmark = needsosdup
 
     def getcapture(self, **kw):
-        cap = capture.StdCaptureFD(**kw)
+        cap = StdCaptureFD(**kw)
         cap.start_capturing()
         return cap
 
@@ -899,18 +907,10 @@
     def test_many(self, capfd):
         with lsof_check():
             for i in range(10):
-                cap = capture.StdCaptureFD()
+                cap = StdCaptureFD()
                 cap.reset()
 
 
- at needsosdup
-def test_stdcapture_fd_tmpfile(tmpfile):
-    capfd = capture.StdCaptureFD(out=tmpfile)
-    os.write(1, "hello".encode("ascii"))
-    os.write(2, "world".encode("ascii"))
-    outf, errf = capfd.stop_capturing()
-    assert outf == tmpfile
-
 
 class TestStdCaptureFDinvalidFD:
     pytestmark = needsosdup
@@ -918,7 +918,10 @@
     def test_stdcapture_fd_invalid_fd(self, testdir):
         testdir.makepyfile("""
             import os
-            from _pytest.capture import StdCaptureFD
+            from _pytest import capture
+            def StdCaptureFD(out=True, err=True, in_=True):
+                return capture.StdCaptureBase(out, err, in_,
+                                              Capture=capture.FDCapture)
             def test_stdout():
                 os.close(1)
                 cap = StdCaptureFD(out=True, err=False, in_=False)
@@ -938,27 +941,12 @@
 
 
 def test_capture_not_started_but_reset():
-    capsys = capture.StdCapture()
+    capsys = StdCapture()
     capsys.stop_capturing()
     capsys.stop_capturing()
     capsys.reset()
 
 
- at needsosdup
-def test_capture_no_sys():
-    capsys = capture.StdCapture()
-    try:
-        cap = capture.StdCaptureFD(patchsys=False)
-        cap.start_capturing()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        oswritebytes(1, "1")
-        oswritebytes(2, "2")
-        out, err = cap.reset()
-        assert out == "1"
-        assert err == "2"
-    finally:
-        capsys.reset()
 
 
 @needsosdup
@@ -966,7 +954,7 @@
 def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
     if not use:
         tmpfile = True
-    cap = capture.StdCaptureFD(out=False, err=tmpfile)
+    cap = StdCaptureFD(out=False, err=tmpfile)
     try:
         cap.start_capturing()
         capfile = cap.err.tmpfile
@@ -977,7 +965,7 @@
     assert capfile2 == capfile
 
 
- at pytest.mark.parametrize('method', ['StdCapture', 'StdCaptureFD'])
+ at pytest.mark.parametrize('method', ['SysCapture', 'FDCapture'])
 def test_capturing_and_logging_fundamentals(testdir, method):
     if method == "StdCaptureFD" and not hasattr(os, 'dup'):
         pytest.skip("need os.dup")
@@ -986,7 +974,8 @@
         import sys, os
         import py, logging
         from _pytest import capture
-        cap = capture.%s(out=False, in_=False)
+        cap = capture.StdCaptureBase(out=False, in_=False,
+                                     Capture=capture.%s)
         cap.start_capturing()
 
         logging.warn("hello1")


https://bitbucket.org/hpk42/pytest/commits/9d11be8dcb85/
Changeset:   9d11be8dcb85
User:        hpk42
Date:        2014-03-28 07:11:25
Summary:     simplify reset/stop_capturing and fix capturing wrt to capturing simple os.write() calls
Affected #:  2 files

diff -r 080d7f3e486f387bb501369a0444e32313e59a7d -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -6,7 +6,7 @@
 
 import sys
 import os
-import tempfile
+from tempfile import TemporaryFile
 import contextlib
 
 import py
@@ -101,12 +101,6 @@
 
 
 
-def maketmpfile():
-    f = py.std.tempfile.TemporaryFile()
-    newf = dupfile(f, encoding="UTF-8")
-    f.close()
-    return newf
-
 class CaptureManager:
     def __init__(self, defaultmethod=None):
         self._method2capture = {}
@@ -137,7 +131,7 @@
     def reset_capturings(self):
         for cap in self._method2capture.values():
             cap.pop_outerr_to_orig()
-            cap.reset()
+            cap.stop_capturing()
         self._method2capture.clear()
 
     def resumecapture_item(self, item):
@@ -274,15 +268,16 @@
 
 class CaptureFixture:
     def __init__(self, captureclass):
-        self._capture = StdCaptureBase(out=True, err=True, in_=False,
-                                   Capture=captureclass)
+        self.captureclass = captureclass
 
     def _start(self):
+        self._capture = StdCaptureBase(out=True, err=True, in_=False,
+                                       Capture=self.captureclass)
         self._capture.start_capturing()
 
     def _finalize(self):
         if hasattr(self, '_capture'):
-            outerr = self._outerr = self._capture.reset()
+            outerr = self._outerr = self._capture.stop_capturing()
             del self._capture
             return outerr
 
@@ -355,21 +350,6 @@
         if err:
             self.err = Capture(2)
 
-    def reset(self):
-        """ reset sys.stdout/stderr and return captured output as strings. """
-        if hasattr(self, '_reset'):
-            raise ValueError("was already reset")
-        self._reset = True
-        outfile, errfile = self.stop_capturing()
-        out, err = "", ""
-        if outfile and not outfile.closed:
-            out = outfile.read()
-            outfile.close()
-        if errfile and errfile != outfile and not errfile.closed:
-            err = errfile.read()
-            errfile.close()
-        return out, err
-
     def start_capturing(self):
         if self.in_:
             self.in_.start()
@@ -378,17 +358,6 @@
         if self.err:
             self.err.start()
 
-    def stop_capturing(self):
-        """ return (outfile, errfile) and stop capturing. """
-        outfile = errfile = None
-        if self.out:
-            outfile = self.out.done()
-        if self.err:
-            errfile = self.err.done()
-        if self.in_:
-            self.in_.done()
-        return outfile, errfile
-
     def pop_outerr_to_orig(self):
         """ pop current snapshot out/err capture and flush to orig streams. """
         out, err = self.readouterr()
@@ -397,25 +366,27 @@
         if err:
             self.err.writeorg(err)
 
+    def stop_capturing(self):
+        """ stop capturing and reset capturing streams """
+        if hasattr(self, '_reset'):
+            raise ValueError("was already stopped")
+        self._reset = True
+        if self.out:
+            self.out.done()
+        if self.err:
+            self.err.done()
+        if self.in_:
+            self.in_.done()
+
     def readouterr(self):
         """ return snapshot unicode value of stdout/stderr capturings. """
         return self._readsnapshot('out'), self._readsnapshot('err')
 
     def _readsnapshot(self, name):
-        try:
-            f = getattr(self, name).tmpfile
-        except AttributeError:
-            return ''
-        if f.tell() == 0:
-            return ''
-        f.seek(0)
-        res = f.read()
-        enc = getattr(f, "encoding", None)
-        if enc and isinstance(res, bytes):
-            res = py.builtin._totext(res, enc, "replace")
-        f.truncate(0)
-        f.seek(0)
-        return res
+        cap = getattr(self, name, None)
+        if cap is None:
+            return ""
+        return cap.snap()
 
 
 class FDCapture:
@@ -433,11 +404,16 @@
                 if targetfd == 0:
                     tmpfile = open(os.devnull, "r")
                 else:
-                    tmpfile = maketmpfile()
+                    f = TemporaryFile()
+                    with f:
+                        tmpfile = dupfile(f, encoding="UTF-8")
             self.tmpfile = tmpfile
             if targetfd in patchsysdict:
                 self._oldsys = getattr(sys, patchsysdict[targetfd])
 
+    def __repr__(self):
+        return "<FDCapture %s oldfd=%s>" % (self.targetfd, self._savefd)
+
     def start(self):
         """ Start capturing on targetfd using memorized tmpfile. """
         try:
@@ -450,16 +426,26 @@
             subst = self.tmpfile if targetfd != 0 else DontReadFromInput()
             setattr(sys, patchsysdict[targetfd], subst)
 
+    def snap(self):
+        f = self.tmpfile
+        f.seek(0)
+        res = f.read()
+        if res:
+            enc = getattr(f, "encoding", None)
+            if enc and isinstance(res, bytes):
+                res = py.builtin._totext(res, enc, "replace")
+            f.truncate(0)
+            f.seek(0)
+        return res
+
     def done(self):
         """ stop capturing, restore streams, return original capture file,
         seeked to position zero. """
         os.dup2(self._savefd, self.targetfd)
         os.close(self._savefd)
-        if self.targetfd != 0:
-            self.tmpfile.seek(0)
         if hasattr(self, '_oldsys'):
             setattr(sys, patchsysdict[self.targetfd], self._oldsys)
-        return self.tmpfile
+        self.tmpfile.close()
 
     def writeorg(self, data):
         """ write a string to the original file descriptor
@@ -482,18 +468,22 @@
     def start(self):
         setattr(sys, self.name, self.tmpfile)
 
+    def snap(self):
+        f = self.tmpfile
+        res = f.getvalue()
+        f.truncate(0)
+        f.seek(0)
+        return res
+
     def done(self):
         setattr(sys, self.name, self._old)
-        if self.name != "stdin":
-            self.tmpfile.seek(0)
-        return self.tmpfile
+        self.tmpfile.close()
 
     def writeorg(self, data):
         self._old.write(data)
         self._old.flush()
 
 
-
 class DontReadFromInput:
     """Temporary stub class.  Ideally when stdin is accessed, the
     capturing should be turned off, with possibly all data captured

diff -r 080d7f3e486f387bb501369a0444e32313e59a7d -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -103,7 +103,7 @@
             assert not out and not err
             capman.reset_capturings()
         finally:
-            capouter.reset()
+            capouter.stop_capturing()
 
     @needsosdup
     def test_juggle_capturings(self, testdir):
@@ -127,7 +127,7 @@
             finally:
                 capman.reset_capturings()
         finally:
-            capouter.reset()
+            capouter.stop_capturing()
 
 
 @pytest.mark.parametrize("method", ['fd', 'sys'])
@@ -696,17 +696,15 @@
         cap = capture.FDCapture(fd)
         data = tobytes("hello")
         os.write(fd, data)
-        f = cap.done()
-        s = f.read()
-        f.close()
+        s = cap.snap()
+        cap.done()
         assert not s
         cap = capture.FDCapture(fd)
         cap.start()
         os.write(fd, data)
-        f = cap.done()
-        s = f.read()
+        s = cap.snap()
+        cap.done()
         assert s == "hello"
-        f.close()
 
     def test_simple_many(self, tmpfile):
         for i in range(10):
@@ -720,16 +718,15 @@
     def test_simple_fail_second_start(self, tmpfile):
         fd = tmpfile.fileno()
         cap = capture.FDCapture(fd)
-        f = cap.done()
+        cap.done()
         pytest.raises(ValueError, cap.start)
-        f.close()
 
     def test_stderr(self):
         cap = capture.FDCapture(2)
         cap.start()
         print_("hello", file=sys.stderr)
-        f = cap.done()
-        s = f.read()
+        s = cap.snap()
+        cap.done()
         assert s == "hello\n"
 
     def test_stdin(self, tmpfile):
@@ -752,8 +749,8 @@
             cap.writeorg(data2)
         finally:
             tmpfile.close()
-        f = cap.done()
-        scap = f.read()
+        scap = cap.snap()
+        cap.done()
         assert scap == totext(data1)
         stmp = open(tmpfile.name, 'rb').read()
         assert stmp == data2
@@ -769,17 +766,17 @@
         cap = self.getcapture()
         sys.stdout.write("hello")
         sys.stderr.write("world")
-        outfile, errfile = cap.stop_capturing()
-        s = outfile.read()
-        assert s == "hello"
-        s = errfile.read()
-        assert s == "world"
+        out, err = cap.readouterr()
+        cap.stop_capturing()
+        assert out == "hello"
+        assert err == "world"
 
     def test_capturing_reset_simple(self):
         cap = self.getcapture()
         print("hello world")
         sys.stderr.write("hello error\n")
-        out, err = cap.reset()
+        out, err = cap.readouterr()
+        cap.stop_capturing()
         assert out == "hello world\n"
         assert err == "hello error\n"
 
@@ -792,8 +789,9 @@
             assert out == "hello world\n"
             assert err == "hello error\n"
             sys.stderr.write("error2")
+            out, err = cap.readouterr()
         finally:
-            out, err = cap.reset()
+            cap.stop_capturing()
         assert err == "error2"
 
     def test_capturing_readouterr_unicode(self):
@@ -802,7 +800,7 @@
             print ("hx\xc4\x85\xc4\x87")
             out, err = cap.readouterr()
         finally:
-            cap.reset()
+            cap.stop_capturing()
         assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
 
     @pytest.mark.skipif('sys.version_info >= (3,)',
@@ -813,13 +811,14 @@
         print('\xa6')
         out, err = cap.readouterr()
         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
-        cap.reset()
+        cap.stop_capturing()
 
     def test_reset_twice_error(self):
         cap = self.getcapture()
         print ("hello")
-        out, err = cap.reset()
-        pytest.raises(ValueError, cap.reset)
+        out, err = cap.readouterr()
+        cap.stop_capturing()
+        pytest.raises(ValueError, cap.stop_capturing)
         assert out == "hello\n"
         assert not err
 
@@ -833,7 +832,8 @@
         sys.stderr = capture.TextIO()
         print ("not seen")
         sys.stderr.write("not seen\n")
-        out, err = cap.reset()
+        out, err = cap.readouterr()
+        cap.stop_capturing()
         assert out == "hello"
         assert err == "world"
         assert sys.stdout == oldout
@@ -844,8 +844,10 @@
         print ("cap1")
         cap2 = self.getcapture()
         print ("cap2")
-        out2, err2 = cap2.reset()
-        out1, err1 = cap1.reset()
+        out2, err2 = cap2.readouterr()
+        out1, err1 = cap1.readouterr()
+        cap2.stop_capturing()
+        cap1.stop_capturing()
         assert out1 == "cap1\n"
         assert out2 == "cap2\n"
 
@@ -853,7 +855,8 @@
         cap = self.getcapture(out=True, err=False)
         sys.stdout.write("hello")
         sys.stderr.write("world")
-        out, err = cap.reset()
+        out, err = cap.readouterr()
+        cap.stop_capturing()
         assert out == "hello"
         assert not err
 
@@ -861,7 +864,8 @@
         cap = self.getcapture(out=False, err=True)
         sys.stdout.write("hello")
         sys.stderr.write("world")
-        out, err = cap.reset()
+        out, err = cap.readouterr()
+        cap.stop_capturing()
         assert err == "world"
         assert not out
 
@@ -869,7 +873,7 @@
         old = sys.stdin
         cap = self.getcapture(in_=True)
         newstdin = sys.stdin
-        out, err = cap.reset()
+        cap.stop_capturing()
         assert newstdin != sys.stdin
         assert sys.stdin is old
 
@@ -879,7 +883,7 @@
         print ("XXX mechanisms")
         cap = self.getcapture()
         pytest.raises(IOError, "sys.stdin.read()")
-        out, err = cap.reset()
+        cap.stop_capturing()
 
 
 class TestStdCaptureFD(TestStdCapture):
@@ -890,6 +894,20 @@
         cap.start_capturing()
         return cap
 
+    def test_simple_only_fd(self, testdir):
+        testdir.makepyfile("""
+            import os
+            def test_x():
+                os.write(1, "hello\\n".encode("ascii"))
+                assert 0
+        """)
+        result = testdir.runpytest()
+        result.stdout.fnmatch_lines("""
+            *test_x*
+            *assert 0*
+            *Captured stdout*
+        """)
+
     def test_intermingling(self):
         cap = self.getcapture()
         oswritebytes(1, "1")
@@ -900,7 +918,8 @@
         sys.stderr.write("b")
         sys.stderr.flush()
         oswritebytes(2, "c")
-        out, err = cap.reset()
+        out, err = cap.readouterr()
+        cap.stop_capturing()
         assert out == "123"
         assert err == "abc"
 
@@ -908,7 +927,7 @@
         with lsof_check():
             for i in range(10):
                 cap = StdCaptureFD()
-                cap.reset()
+                cap.stop_capturing()
 
 
 
@@ -943,10 +962,6 @@
 def test_capture_not_started_but_reset():
     capsys = StdCapture()
     capsys.stop_capturing()
-    capsys.stop_capturing()
-    capsys.reset()
-
-
 
 
 @needsosdup
@@ -960,7 +975,7 @@
         capfile = cap.err.tmpfile
         cap.readouterr()
     finally:
-        cap.reset()
+        cap.stop_capturing()
     capfile2 = cap.err.tmpfile
     assert capfile2 == capfile
 


https://bitbucket.org/hpk42/pytest/commits/f9dfeecab061/
Changeset:   f9dfeecab061
User:        hpk42
Date:        2014-03-28 07:13:08
Summary:     simplify capturing funcarg handling
Affected #:  1 file

diff -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee -r f9dfeecab0611e3f4a8f0f369ad0cf14beba3820 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -155,29 +155,23 @@
 
     def suspendcapture(self, item=None):
         self.deactivate_funcargs()
-        if hasattr(self, '_capturing'):
-            method = self._capturing
-            del self._capturing
+        method = self.__dict__.pop("_capturing", None)
+        if method is not None:
             cap = self._method2capture.get(method)
             if cap is not None:
                 return cap.readouterr()
         return "", ""
 
     def activate_funcargs(self, pyfuncitem):
-        funcargs = getattr(pyfuncitem, "funcargs", None)
-        if funcargs is not None:
-            for name, capfuncarg in funcargs.items():
-                if name in ('capsys', 'capfd'):
-                    assert not hasattr(self, '_capturing_funcarg')
-                    self._capturing_funcarg = capfuncarg
-                    capfuncarg._start()
+        capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
+        if capfuncarg is not None:
+            capfuncarg._start()
+            self._capfuncarg = capfuncarg
 
     def deactivate_funcargs(self):
-        capturing_funcarg = getattr(self, '_capturing_funcarg', None)
-        if capturing_funcarg:
-            outerr = capturing_funcarg._finalize()
-            del self._capturing_funcarg
-            return outerr
+        capfuncarg = self.__dict__.pop("_capfuncarg", None)
+        if capfuncarg is not None:
+            capfuncarg.close()
 
     @pytest.mark.hookwrapper
     def pytest_make_collect_report(self, __multicall__, collector):
@@ -210,7 +204,9 @@
     @pytest.mark.hookwrapper
     def pytest_runtest_call(self, item):
         with self.item_capture_wrapper(item, "call"):
+            self.activate_funcargs(item)
             yield
+            #self.deactivate_funcargs() called from ctx's suspendcapture()
 
     @pytest.mark.hookwrapper
     def pytest_runtest_teardown(self, item):
@@ -228,17 +224,8 @@
     @contextlib.contextmanager
     def item_capture_wrapper(self, item, when):
         self.resumecapture_item(item)
-        if when == "call":
-            self.activate_funcargs(item)
-            yield
-            funcarg_outerr = self.deactivate_funcargs()
-        else:
-            yield
-            funcarg_outerr = None
+        yield
         out, err = self.suspendcapture(item)
-        if funcarg_outerr is not None:
-            out += funcarg_outerr[0]
-            err += funcarg_outerr[1]
         item.add_report_section(when, "out", out)
         item.add_report_section(when, "err", err)
 
@@ -252,7 +239,8 @@
     """
     if "capfd" in request._funcargs:
         raise request.raiseerror(error_capsysfderror)
-    return CaptureFixture(SysCapture)
+    request.node._capfuncarg = c = CaptureFixture(SysCapture)
+    return c
 
 def pytest_funcarg__capfd(request):
     """enables capturing of writes to file descriptors 1 and 2 and makes
@@ -263,7 +251,8 @@
         request.raiseerror(error_capsysfderror)
     if not hasattr(os, 'dup'):
         pytest.skip("capfd funcarg needs os.dup")
-    return CaptureFixture(FDCapture)
+    request.node._capfuncarg = c = CaptureFixture(FDCapture)
+    return c
 
 
 class CaptureFixture:
@@ -275,21 +264,17 @@
                                        Capture=self.captureclass)
         self._capture.start_capturing()
 
-    def _finalize(self):
-        if hasattr(self, '_capture'):
-            outerr = self._outerr = self._capture.stop_capturing()
-            del self._capture
-            return outerr
+    def close(self):
+        cap = self.__dict__.pop("_capture", None)
+        if cap is not None:
+            cap.pop_outerr_to_orig()
+            cap.stop_capturing()
 
     def readouterr(self):
         try:
             return self._capture.readouterr()
         except AttributeError:
-            return self._outerr
-
-    def close(self):
-        self._finalize()
-
+            return "", ""
 
 
 def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
@@ -448,8 +433,7 @@
         self.tmpfile.close()
 
     def writeorg(self, data):
-        """ write a string to the original file descriptor
-        """
+        """ write to original file descriptor. """
         if py.builtin._istext(data):
             data = data.encode("utf8") # XXX use encoding of original stream
         os.write(self._savefd, data)


https://bitbucket.org/hpk42/pytest/commits/d3ebd61be2a1/
Changeset:   d3ebd61be2a1
User:        hpk42
Date:        2014-03-28 07:55:07
Summary:     simplify some capturing tests
Affected #:  1 file

diff -r f9dfeecab0611e3f4a8f0f369ad0cf14beba3820 -r d3ebd61be2a14ea1bceb3acb7bb7caee98cae914 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -757,32 +757,35 @@
 
 
 class TestStdCapture:
+    captureclass = staticmethod(StdCapture)
+
+    @contextlib.contextmanager
     def getcapture(self, **kw):
-        cap = StdCapture(**kw)
+        cap = self.__class__.captureclass(**kw)
         cap.start_capturing()
-        return cap
+        try:
+            yield cap
+        finally:
+            cap.stop_capturing()
 
     def test_capturing_done_simple(self):
-        cap = self.getcapture()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
         assert out == "hello"
         assert err == "world"
 
     def test_capturing_reset_simple(self):
-        cap = self.getcapture()
-        print("hello world")
-        sys.stderr.write("hello error\n")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            print("hello world")
+            sys.stderr.write("hello error\n")
+            out, err = cap.readouterr()
         assert out == "hello world\n"
         assert err == "hello error\n"
 
     def test_capturing_readouterr(self):
-        cap = self.getcapture()
-        try:
+        with self.getcapture() as cap:
             print ("hello world")
             sys.stderr.write("hello error\n")
             out, err = cap.readouterr()
@@ -790,34 +793,27 @@
             assert err == "hello error\n"
             sys.stderr.write("error2")
             out, err = cap.readouterr()
-        finally:
-            cap.stop_capturing()
         assert err == "error2"
 
     def test_capturing_readouterr_unicode(self):
-        cap = self.getcapture()
-        try:
+        with self.getcapture() as cap:
             print ("hx\xc4\x85\xc4\x87")
             out, err = cap.readouterr()
-        finally:
-            cap.stop_capturing()
         assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
 
     @pytest.mark.skipif('sys.version_info >= (3,)',
                         reason='text output different for bytes on python3')
     def test_capturing_readouterr_decode_error_handling(self):
-        cap = self.getcapture()
-        # triggered a internal error in pytest
-        print('\xa6')
-        out, err = cap.readouterr()
+        with self.getcapture() as cap:
+            # triggered a internal error in pytest
+            print('\xa6')
+            out, err = cap.readouterr()
         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
-        cap.stop_capturing()
 
     def test_reset_twice_error(self):
-        cap = self.getcapture()
-        print ("hello")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            print ("hello")
+            out, err = cap.readouterr()
         pytest.raises(ValueError, cap.stop_capturing)
         assert out == "hello\n"
         assert not err
@@ -825,55 +821,49 @@
     def test_capturing_modify_sysouterr_in_between(self):
         oldout = sys.stdout
         olderr = sys.stderr
-        cap = self.getcapture()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        sys.stdout = capture.TextIO()
-        sys.stderr = capture.TextIO()
-        print ("not seen")
-        sys.stderr.write("not seen\n")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            sys.stdout = capture.TextIO()
+            sys.stderr = capture.TextIO()
+            print ("not seen")
+            sys.stderr.write("not seen\n")
+            out, err = cap.readouterr()
         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.readouterr()
-        out1, err1 = cap1.readouterr()
-        cap2.stop_capturing()
-        cap1.stop_capturing()
+        with self.getcapture() as cap1:
+            print ("cap1")
+            with self.getcapture() as cap2:
+                print ("cap2")
+                out2, err2 = cap2.readouterr()
+                out1, err1 = cap1.readouterr()
         assert out1 == "cap1\n"
         assert out2 == "cap2\n"
 
     def test_just_out_capture(self):
-        cap = self.getcapture(out=True, err=False)
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture(out=True, err=False) as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
         assert out == "hello"
         assert not err
 
     def test_just_err_capture(self):
-        cap = self.getcapture(out=False, err=True)
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture(out=False, err=True) as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
         assert err == "world"
         assert not out
 
     def test_stdin_restored(self):
         old = sys.stdin
-        cap = self.getcapture(in_=True)
-        newstdin = sys.stdin
-        cap.stop_capturing()
+        with self.getcapture(in_=True) as cap:
+            newstdin = sys.stdin
         assert newstdin != sys.stdin
         assert sys.stdin is old
 
@@ -881,18 +871,13 @@
         print ("XXX this test may well hang instead of crashing")
         print ("XXX which indicates an error in the underlying capturing")
         print ("XXX mechanisms")
-        cap = self.getcapture()
-        pytest.raises(IOError, "sys.stdin.read()")
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            pytest.raises(IOError, "sys.stdin.read()")
 
 
 class TestStdCaptureFD(TestStdCapture):
     pytestmark = needsosdup
-
-    def getcapture(self, **kw):
-        cap = StdCaptureFD(**kw)
-        cap.start_capturing()
-        return cap
+    captureclass = staticmethod(StdCaptureFD)
 
     def test_simple_only_fd(self, testdir):
         testdir.makepyfile("""
@@ -909,17 +894,16 @@
         """)
 
     def test_intermingling(self):
-        cap = self.getcapture()
-        oswritebytes(1, "1")
-        sys.stdout.write(str(2))
-        sys.stdout.flush()
-        oswritebytes(1, "3")
-        oswritebytes(2, "a")
-        sys.stderr.write("b")
-        sys.stderr.flush()
-        oswritebytes(2, "c")
-        out, err = cap.readouterr()
-        cap.stop_capturing()
+        with self.getcapture() as cap:
+            oswritebytes(1, "1")
+            sys.stdout.write(str(2))
+            sys.stdout.flush()
+            oswritebytes(1, "3")
+            oswritebytes(2, "a")
+            sys.stderr.write("b")
+            sys.stderr.flush()
+            oswritebytes(2, "c")
+            out, err = cap.readouterr()
         assert out == "123"
         assert err == "abc"
 
@@ -930,7 +914,6 @@
                 cap.stop_capturing()
 
 
-
 class TestStdCaptureFDinvalidFD:
     pytestmark = needsosdup
 


https://bitbucket.org/hpk42/pytest/commits/756cdf331fc8/
Changeset:   756cdf331fc8
User:        hpk42
Date:        2014-03-28 09:27:44
Summary:     merge
Affected #:  16 files

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -1,12 +1,13 @@
 """
-    per-test stdout/stderr capturing mechanisms,
-    ``capsys`` and ``capfd`` function arguments.
+per-test stdout/stderr capturing mechanism.
+
 """
-# note: py.io capture was where copied from
-# pylib 1.4.20.dev2 (rev 13d9af95547e)
+from __future__ import with_statement
+
 import sys
 import os
-import tempfile
+from tempfile import TemporaryFile
+import contextlib
 
 import py
 import pytest
@@ -58,8 +59,18 @@
         method = "fd"
     if method == "fd" and not hasattr(os, "dup"):
         method = "sys"
+    pluginmanager = early_config.pluginmanager
+    if method != "no":
+        try:
+            sys.stdout.fileno()
+        except Exception:
+            dupped_stdout = sys.stdout
+        else:
+            dupped_stdout = dupfile(sys.stdout, buffering=1)
+        pluginmanager.register(dupped_stdout, "dupped_stdout")
+            #pluginmanager.add_shutdown(dupped_stdout.close)
     capman = CaptureManager(method)
-    early_config.pluginmanager.register(capman, "capturemanager")
+    pluginmanager.register(capman, "capturemanager")
 
     # make sure that capturemanager is properly reset at final shutdown
     def teardown():
@@ -68,13 +79,13 @@
         except ValueError:
             pass
 
-    early_config.pluginmanager.add_shutdown(teardown)
+    pluginmanager.add_shutdown(teardown)
 
     # make sure logging does not raise exceptions at the end
     def silence_logging_at_shutdown():
         if "logging" in sys.modules:
             sys.modules["logging"].raiseExceptions = False
-    early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown)
+    pluginmanager.add_shutdown(silence_logging_at_shutdown)
 
     # finally trigger conftest loading but while capturing (issue93)
     capman.resumecapture()
@@ -89,53 +100,19 @@
         raise
 
 
-def addouterr(rep, outerr):
-    for secname, content in zip(["out", "err"], outerr):
-        if content:
-            rep.sections.append(("Captured std%s" % secname, content))
-
-
-class NoCapture:
-    def startall(self):
-        pass
-
-    def resume(self):
-        pass
-
-    def reset(self):
-        pass
-
-    def suspend(self):
-        return "", ""
-
 
 class CaptureManager:
     def __init__(self, defaultmethod=None):
         self._method2capture = {}
         self._defaultmethod = defaultmethod
 
-    def _maketempfile(self):
-        f = py.std.tempfile.TemporaryFile()
-        newf = dupfile(f, encoding="UTF-8")
-        f.close()
-        return newf
-
-    def _makestringio(self):
-        return TextIO()
-
     def _getcapture(self, method):
         if method == "fd":
-            return StdCaptureFD(
-                out=self._maketempfile(),
-                err=self._maketempfile(),
-            )
+            return StdCaptureBase(out=True, err=True, Capture=FDCapture)
         elif method == "sys":
-            return StdCapture(
-                out=self._makestringio(),
-                err=self._makestringio(),
-            )
+            return StdCaptureBase(out=True, err=True, Capture=SysCapture)
         elif method == "no":
-            return NoCapture()
+            return StdCaptureBase(out=False, err=False, in_=False)
         else:
             raise ValueError("unknown capturing method: %r" % method)
 
@@ -153,12 +130,12 @@
 
     def reset_capturings(self):
         for cap in self._method2capture.values():
-            cap.reset()
+            cap.pop_outerr_to_orig()
+            cap.stop_capturing()
+        self._method2capture.clear()
 
     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):
@@ -172,87 +149,85 @@
         self._capturing = method
         if cap is None:
             self._method2capture[method] = cap = self._getcapture(method)
-            cap.startall()
+            cap.start_capturing()
         else:
-            cap.resume()
+            cap.pop_outerr_to_orig()
 
     def suspendcapture(self, item=None):
         self.deactivate_funcargs()
-        if hasattr(self, '_capturing'):
-            method = self._capturing
+        method = self.__dict__.pop("_capturing", None)
+        if method is not None:
             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.readouterr()
         return "", ""
 
     def activate_funcargs(self, pyfuncitem):
-        funcargs = getattr(pyfuncitem, "funcargs", None)
-        if funcargs is not None:
-            for name, capfuncarg in funcargs.items():
-                if name in ('capsys', 'capfd'):
-                    assert not hasattr(self, '_capturing_funcarg')
-                    self._capturing_funcarg = capfuncarg
-                    capfuncarg._start()
+        capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
+        if capfuncarg is not None:
+            capfuncarg._start()
+            self._capfuncarg = capfuncarg
 
     def deactivate_funcargs(self):
-        capturing_funcarg = getattr(self, '_capturing_funcarg', None)
-        if capturing_funcarg:
-            outerr = capturing_funcarg._finalize()
-            del self._capturing_funcarg
-            return outerr
+        capfuncarg = self.__dict__.pop("_capfuncarg", None)
+        if capfuncarg is not None:
+            capfuncarg.close()
 
+    @pytest.mark.hookwrapper
     def pytest_make_collect_report(self, __multicall__, collector):
         method = self._getmethod(collector.config, collector.fspath)
         try:
             self.resumecapture(method)
         except ValueError:
+            yield
             # recursive collect, XXX refactor capturing
             # to allow for more lightweight recursive capturing
             return
-        try:
-            rep = __multicall__.execute()
-        finally:
-            outerr = self.suspendcapture()
-        addouterr(rep, outerr)
-        return rep
+        yield
+        out, err = self.suspendcapture()
+        # XXX getting the report from the ongoing hook call is a bit
+        # of a hack.  We need to think about capturing during collection
+        # and find out if it's really needed fine-grained (per
+        # collector).
+        if __multicall__.results:
+            rep = __multicall__.results[0]
+            if out:
+                rep.sections.append(("Captured stdout", out))
+            if err:
+                rep.sections.append(("Captured stderr", err))
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_setup(self, item):
+        with self.item_capture_wrapper(item, "setup"):
+            yield
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_call(self, item):
+        with self.item_capture_wrapper(item, "call"):
+            self.activate_funcargs(item)
+            yield
+            #self.deactivate_funcargs() called from ctx's suspendcapture()
+
+    @pytest.mark.hookwrapper
+    def pytest_runtest_teardown(self, item):
+        with self.item_capture_wrapper(item, "teardown"):
+            yield
 
     @pytest.mark.tryfirst
-    def pytest_runtest_setup(self, item):
-        self.resumecapture_item(item)
+    def pytest_keyboard_interrupt(self, excinfo):
+        self.reset_capturings()
 
     @pytest.mark.tryfirst
-    def pytest_runtest_call(self, item):
+    def pytest_internalerror(self, excinfo):
+        self.reset_capturings()
+
+    @contextlib.contextmanager
+    def item_capture_wrapper(self, item, when):
         self.resumecapture_item(item)
-        self.activate_funcargs(item)
-
-    @pytest.mark.tryfirst
-    def pytest_runtest_teardown(self, item):
-        self.resumecapture_item(item)
-
-    def pytest_keyboard_interrupt(self, excinfo):
-        if hasattr(self, '_capturing'):
-            self.suspendcapture()
-
-    @pytest.mark.tryfirst
-    def pytest_runtest_makereport(self, __multicall__, item, call):
-        funcarg_outerr = self.deactivate_funcargs()
-        rep = __multicall__.execute()
-        outerr = 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
+        yield
+        out, err = self.suspendcapture(item)
+        item.add_report_section(when, "out", out)
+        item.add_report_section(when, "err", err)
 
 error_capsysfderror = "cannot use capsys and capfd at the same time"
 
@@ -264,8 +239,8 @@
     """
     if "capfd" in request._funcargs:
         raise request.raiseerror(error_capsysfderror)
-    return CaptureFixture(StdCapture)
-
+    request.node._capfuncarg = c = CaptureFixture(SysCapture)
+    return c
 
 def pytest_funcarg__capfd(request):
     """enables capturing of writes to file descriptors 1 and 2 and makes
@@ -276,89 +251,30 @@
         request.raiseerror(error_capsysfderror)
     if not hasattr(os, 'dup'):
         pytest.skip("capfd funcarg needs os.dup")
-    return CaptureFixture(StdCaptureFD)
+    request.node._capfuncarg = c = CaptureFixture(FDCapture)
+    return c
 
 
 class CaptureFixture:
     def __init__(self, captureclass):
-        self._capture = captureclass()
+        self.captureclass = captureclass
 
     def _start(self):
-        self._capture.startall()
+        self._capture = StdCaptureBase(out=True, err=True, in_=False,
+                                       Capture=self.captureclass)
+        self._capture.start_capturing()
 
-    def _finalize(self):
-        if hasattr(self, '_capture'):
-            outerr = self._outerr = self._capture.reset()
-            del self._capture
-            return outerr
+    def close(self):
+        cap = self.__dict__.pop("_capture", None)
+        if cap is not None:
+            cap.pop_outerr_to_orig()
+            cap.stop_capturing()
 
     def readouterr(self):
         try:
             return self._capture.readouterr()
         except AttributeError:
-            return self._outerr
-
-    def close(self):
-        self._finalize()
-
-
-class FDCapture:
-    """ Capture IO to/from a given os-level filedescriptor. """
-
-    def __init__(self, targetfd, tmpfile=None, patchsys=False):
-        """ save targetfd descriptor, and open a new
-            temporary file there.  If no tmpfile is
-            specified a tempfile.Tempfile() will be opened
-            in text mode.
-        """
-        self.targetfd = targetfd
-        if tmpfile is None and targetfd != 0:
-            f = tempfile.TemporaryFile('wb+')
-            tmpfile = dupfile(f, encoding="UTF-8")
-            f.close()
-        self.tmpfile = tmpfile
-        self._savefd = os.dup(self.targetfd)
-        if patchsys:
-            self._oldsys = getattr(sys, patchsysdict[targetfd])
-
-    def start(self):
-        try:
-            os.fstat(self._savefd)
-        except OSError:
-            raise ValueError(
-                "saved filedescriptor not valid, "
-                "did you call start() twice?")
-        if self.targetfd == 0 and not self.tmpfile:
-            fd = os.open(os.devnull, os.O_RDONLY)
-            os.dup2(fd, 0)
-            os.close(fd)
-            if hasattr(self, '_oldsys'):
-                setattr(sys, patchsysdict[self.targetfd], DontReadFromInput())
-        else:
-            os.dup2(self.tmpfile.fileno(), self.targetfd)
-            if hasattr(self, '_oldsys'):
-                setattr(sys, patchsysdict[self.targetfd], self.tmpfile)
-
-    def done(self):
-        """ unpatch and clean up, returns the self.tmpfile (file object)
-        """
-        os.dup2(self._savefd, self.targetfd)
-        os.close(self._savefd)
-        if self.targetfd != 0:
-            self.tmpfile.seek(0)
-        if hasattr(self, '_oldsys'):
-            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
-        return self.tmpfile
-
-    def writeorg(self, data):
-        """ write a string to the original file descriptor
-        """
-        tempfp = tempfile.TemporaryFile()
-        try:
-            os.dup2(self._savefd, tempfp.fileno())
-            tempfp.write(data)
-        finally:
-            tempfp.close()
+            return "", ""
 
 
 def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
@@ -408,185 +324,148 @@
         return getattr(self._stream, name)
 
 
-class Capture(object):
-    def reset(self):
-        """ reset sys.stdout/stderr and return captured output as strings. """
-        if hasattr(self, '_reset'):
-            raise ValueError("was already reset")
-        self._reset = True
-        outfile, errfile = self.done(save=False)
-        out, err = "", ""
-        if outfile and not outfile.closed:
-            out = outfile.read()
-            outfile.close()
-        if errfile and errfile != outfile and not errfile.closed:
-            err = errfile.read()
-            errfile.close()
-        return out, err
+class StdCaptureBase(object):
+    out = err = in_ = None
 
-    def suspend(self):
-        """ return current snapshot captures, memorize tempfiles. """
-        outerr = self.readouterr()
-        outfile, errfile = self.done()
-        return outerr
+    def __init__(self, out=True, err=True, in_=True, Capture=None):
+        if in_:
+            self.in_ = Capture(0)
+        if out:
+            self.out = Capture(1)
+        if err:
+            self.err = Capture(2)
 
-
-class StdCaptureFD(Capture):
-    """ This class allows to capture writes to FD1 and FD2
-        and may connect a NULL file to FD0 (and prevent
-        reads from sys.stdin).  If any of the 0,1,2 file descriptors
-        is invalid it will not be captured.
-    """
-    def __init__(self, out=True, err=True, in_=True, patchsys=True):
-        self._options = {
-            "out": out,
-            "err": err,
-            "in_": in_,
-            "patchsys": patchsys,
-        }
-        self._save()
-
-    def _save(self):
-        in_ = self._options['in_']
-        out = self._options['out']
-        err = self._options['err']
-        patchsys = self._options['patchsys']
-        if in_:
-            try:
-                self.in_ = FDCapture(
-                    0, tmpfile=None,
-                    patchsys=patchsys)
-            except OSError:
-                pass
-        if out:
-            tmpfile = None
-            if hasattr(out, 'write'):
-                tmpfile = out
-            try:
-                self.out = FDCapture(
-                    1, tmpfile=tmpfile,
-                    patchsys=patchsys)
-                self._options['out'] = self.out.tmpfile
-            except OSError:
-                pass
-        if err:
-            if hasattr(err, 'write'):
-                tmpfile = err
-            else:
-                tmpfile = None
-            try:
-                self.err = FDCapture(
-                    2, tmpfile=tmpfile,
-                    patchsys=patchsys)
-                self._options['err'] = self.err.tmpfile
-            except OSError:
-                pass
-
-    def startall(self):
-        if hasattr(self, 'in_'):
+    def start_capturing(self):
+        if self.in_:
             self.in_.start()
-        if hasattr(self, 'out'):
+        if self.out:
             self.out.start()
-        if hasattr(self, 'err'):
+        if self.err:
             self.err.start()
 
-    def resume(self):
-        """ resume capturing with original temp files. """
-        self.startall()
+    def pop_outerr_to_orig(self):
+        """ pop current snapshot out/err capture and flush to orig streams. """
+        out, err = self.readouterr()
+        if out:
+            self.out.writeorg(out)
+        if err:
+            self.err.writeorg(err)
 
-    def done(self, save=True):
-        """ return (outfile, errfile) and stop capturing. """
-        outfile = errfile = None
-        if hasattr(self, 'out') and not self.out.tmpfile.closed:
-            outfile = self.out.done()
-        if hasattr(self, 'err') and not self.err.tmpfile.closed:
-            errfile = self.err.done()
-        if hasattr(self, 'in_'):
+    def stop_capturing(self):
+        """ stop capturing and reset capturing streams """
+        if hasattr(self, '_reset'):
+            raise ValueError("was already stopped")
+        self._reset = True
+        if self.out:
+            self.out.done()
+        if self.err:
+            self.err.done()
+        if self.in_:
             self.in_.done()
-        if save:
-            self._save()
-        return outfile, errfile
 
     def readouterr(self):
-        """ return snapshot value of stdout/stderr capturings. """
-        out = self._readsnapshot('out')
-        err = self._readsnapshot('err')
-        return out, err
+        """ return snapshot unicode value of stdout/stderr capturings. """
+        return self._readsnapshot('out'), self._readsnapshot('err')
 
     def _readsnapshot(self, name):
-        if hasattr(self, name):
-            f = getattr(self, name).tmpfile
+        cap = getattr(self, name, None)
+        if cap is None:
+            return ""
+        return cap.snap()
+
+
+class FDCapture:
+    """ Capture IO to/from a given os-level filedescriptor. """
+
+    def __init__(self, targetfd, tmpfile=None):
+        self.targetfd = targetfd
+        try:
+            self._savefd = os.dup(self.targetfd)
+        except OSError:
+            self.start = lambda: None
+            self.done = lambda: None
         else:
-            return ''
+            if tmpfile is None:
+                if targetfd == 0:
+                    tmpfile = open(os.devnull, "r")
+                else:
+                    f = TemporaryFile()
+                    with f:
+                        tmpfile = dupfile(f, encoding="UTF-8")
+            self.tmpfile = tmpfile
+            if targetfd in patchsysdict:
+                self._oldsys = getattr(sys, patchsysdict[targetfd])
 
+    def __repr__(self):
+        return "<FDCapture %s oldfd=%s>" % (self.targetfd, self._savefd)
+
+    def start(self):
+        """ Start capturing on targetfd using memorized tmpfile. """
+        try:
+            os.fstat(self._savefd)
+        except OSError:
+            raise ValueError("saved filedescriptor not valid anymore")
+        targetfd = self.targetfd
+        os.dup2(self.tmpfile.fileno(), targetfd)
+        if hasattr(self, '_oldsys'):
+            subst = self.tmpfile if targetfd != 0 else DontReadFromInput()
+            setattr(sys, patchsysdict[targetfd], subst)
+
+    def snap(self):
+        f = self.tmpfile
         f.seek(0)
         res = f.read()
-        enc = getattr(f, "encoding", None)
-        if enc:
-            res = py.builtin._totext(res, enc, "replace")
+        if res:
+            enc = getattr(f, "encoding", None)
+            if enc and isinstance(res, bytes):
+                res = py.builtin._totext(res, enc, "replace")
+            f.truncate(0)
+            f.seek(0)
+        return res
+
+    def done(self):
+        """ stop capturing, restore streams, return original capture file,
+        seeked to position zero. """
+        os.dup2(self._savefd, self.targetfd)
+        os.close(self._savefd)
+        if hasattr(self, '_oldsys'):
+            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
+        self.tmpfile.close()
+
+    def writeorg(self, data):
+        """ write to original file descriptor. """
+        if py.builtin._istext(data):
+            data = data.encode("utf8") # XXX use encoding of original stream
+        os.write(self._savefd, data)
+
+
+class SysCapture:
+    def __init__(self, fd):
+        name = patchsysdict[fd]
+        self._old = getattr(sys, name)
+        self.name = name
+        if name == "stdin":
+            self.tmpfile = DontReadFromInput()
+        else:
+            self.tmpfile = TextIO()
+
+    def start(self):
+        setattr(sys, self.name, self.tmpfile)
+
+    def snap(self):
+        f = self.tmpfile
+        res = f.getvalue()
         f.truncate(0)
         f.seek(0)
         return res
 
+    def done(self):
+        setattr(sys, self.name, self._old)
+        self.tmpfile.close()
 
-class StdCapture(Capture):
-    """ This class allows to capture writes to sys.stdout|stderr "in-memory"
-        and will raise errors on tries to read from sys.stdin. It only
-        modifies sys.stdout|stderr|stdin attributes and does not
-        touch underlying File Descriptors (use StdCaptureFD for that).
-    """
-    def __init__(self, out=True, err=True, in_=True):
-        self._oldout = sys.stdout
-        self._olderr = sys.stderr
-        self._oldin = sys.stdin
-        if out and not hasattr(out, 'file'):
-            out = TextIO()
-        self.out = out
-        if err:
-            if not hasattr(err, 'write'):
-                err = TextIO()
-        self.err = err
-        self.in_ = in_
-
-    def startall(self):
-        if self.out:
-            sys.stdout = self.out
-        if self.err:
-            sys.stderr = self.err
-        if self.in_:
-            sys.stdin = self.in_ = DontReadFromInput()
-
-    def done(self, save=True):
-        """ return (outfile, errfile) and stop capturing. """
-        outfile = errfile = None
-        if self.out and not self.out.closed:
-            sys.stdout = self._oldout
-            outfile = self.out
-            outfile.seek(0)
-        if self.err and not self.err.closed:
-            sys.stderr = self._olderr
-            errfile = self.err
-            errfile.seek(0)
-        if self.in_:
-            sys.stdin = self._oldin
-        return outfile, errfile
-
-    def resume(self):
-        """ resume capturing with original temp files. """
-        self.startall()
-
-    def readouterr(self):
-        """ return snapshot value of stdout/stderr capturings. """
-        out = err = ""
-        if self.out:
-            out = self.out.getvalue()
-            self.out.truncate(0)
-            self.out.seek(0)
-        if self.err:
-            err = self.err.getvalue()
-            self.err.truncate(0)
-            self.err.seek(0)
-        return out, err
+    def writeorg(self, data):
+        self._old.write(data)
+        self._old.flush()
 
 
 class DontReadFromInput:

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -56,11 +56,15 @@
             raise ValueError("not a string or argument list: %r" % (args,))
         args = py.std.shlex.split(args)
     pluginmanager = get_plugin_manager()
-    if plugins:
-        for plugin in plugins:
-            pluginmanager.register(plugin)
-    return pluginmanager.hook.pytest_cmdline_parse(
-            pluginmanager=pluginmanager, args=args)
+    try:
+        if plugins:
+            for plugin in plugins:
+                pluginmanager.register(plugin)
+        return pluginmanager.hook.pytest_cmdline_parse(
+                pluginmanager=pluginmanager, args=args)
+    except Exception:
+        pluginmanager.ensure_shutdown()
+        raise
 
 class PytestPluginManager(PluginManager):
     def __init__(self, hookspecs=[hookspec]):
@@ -612,6 +616,9 @@
         self.hook.pytest_logwarning(code=code, message=message,
                                     fslocation=None, nodeid=None)
 
+    def get_terminal_writer(self):
+        return self.pluginmanager.getplugin("terminalreporter")._tw
+
     def pytest_cmdline_parse(self, pluginmanager, args):
         assert self == pluginmanager.config, (self, pluginmanager.config)
         self.parse(args)

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/core.py
--- a/_pytest/core.py
+++ b/_pytest/core.py
@@ -240,18 +240,22 @@
             pass
         l = []
         last = []
+        wrappers = []
         for plugin in plugins:
             try:
                 meth = getattr(plugin, attrname)
-                if hasattr(meth, 'tryfirst'):
-                    last.append(meth)
-                elif hasattr(meth, 'trylast'):
-                    l.insert(0, meth)
-                else:
-                    l.append(meth)
             except AttributeError:
                 continue
+            if hasattr(meth, 'hookwrapper'):
+                wrappers.append(meth)
+            elif hasattr(meth, 'tryfirst'):
+                last.append(meth)
+            elif hasattr(meth, 'trylast'):
+                l.insert(0, meth)
+            else:
+                l.append(meth)
         l.extend(last)
+        l.extend(wrappers)
         self._listattrcache[key] = list(l)
         return l
 
@@ -272,6 +276,14 @@
 
 class MultiCall:
     """ execute a call into multiple python functions/methods. """
+
+    class WrongHookWrapper(Exception):
+        """ a hook wrapper does not behave correctly. """
+        def __init__(self, func, message):
+            Exception.__init__(self, func, message)
+            self.func = func
+            self.message = message
+
     def __init__(self, methods, kwargs, firstresult=False):
         self.methods = list(methods)
         self.kwargs = kwargs
@@ -283,16 +295,39 @@
         return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
 
     def execute(self):
-        while self.methods:
-            method = self.methods.pop()
-            kwargs = self.getkwargs(method)
-            res = method(**kwargs)
-            if res is not None:
-                self.results.append(res)
-                if self.firstresult:
-                    return res
-        if not self.firstresult:
-            return self.results
+        next_finalizers = []
+        try:
+            while self.methods:
+                method = self.methods.pop()
+                kwargs = self.getkwargs(method)
+                if hasattr(method, "hookwrapper"):
+                    it = method(**kwargs)
+                    next = getattr(it, "next", None)
+                    if next is None:
+                        next = getattr(it, "__next__", None)
+                        if next is None:
+                            raise self.WrongHookWrapper(method,
+                                "wrapper does not contain a yield")
+                    res = next()
+                    next_finalizers.append((method, next))
+                else:
+                    res = method(**kwargs)
+                if res is not None:
+                    self.results.append(res)
+                    if self.firstresult:
+                        return res
+            if not self.firstresult:
+                return self.results
+        finally:
+            for method, fin in reversed(next_finalizers):
+                try:
+                    fin()
+                except StopIteration:
+                    pass
+                else:
+                    raise self.WrongHookWrapper(method,
+                                "wrapper contain more than one yield")
+
 
     def getkwargs(self, method):
         kwargs = {}

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -60,6 +60,7 @@
 def pytest_cmdline_main(config):
     genscript = config.getvalue("genscript")
     if genscript:
+        #tw = config.get_terminal_writer()
         tw = py.io.TerminalWriter()
         deps =  ['py', '_pytest', 'pytest']
         if sys.version_info < (2,7):

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -47,6 +47,8 @@
 
 def pytest_cmdline_main(config):
     if config.option.version:
+        capman = config.pluginmanager.getplugin("capturemanager")
+        capman.reset_capturings()
         p = py.path.local(pytest.__file__)
         sys.stderr.write("This is pytest version %s, imported from %s\n" %
             (pytest.__version__, p))
@@ -62,7 +64,7 @@
         return 0
 
 def showhelp(config):
-    tw = py.io.TerminalWriter()
+    tw = config.get_terminal_writer()
     tw.write(config._parser.optparser.format_help())
     tw.line()
     tw.line()

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/junitxml.py
--- a/_pytest/junitxml.py
+++ b/_pytest/junitxml.py
@@ -108,12 +108,14 @@
         ))
 
     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 -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -233,6 +233,7 @@
 
         # used for storing artificial fixturedefs for direct parametrization
         self._name2pseudofixturedef = {}
+
         #self.extrainit()
 
     @property
@@ -465,6 +466,14 @@
     """
     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 -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/mark.py
--- a/_pytest/mark.py
+++ b/_pytest/mark.py
@@ -40,7 +40,7 @@
 def pytest_cmdline_main(config):
     if config.option.markers:
         config.do_configure()
-        tw = py.io.TerminalWriter()
+        tw = config.get_terminal_writer()
         for line in config.getini("markers"):
             name, rest = line.split(":", 1)
             tw.write("@pytest.mark.%s:" % name, bold=True)

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/pdb.py
--- a/_pytest/pdb.py
+++ b/_pytest/pdb.py
@@ -16,49 +16,36 @@
     if config.getvalue("usepdb"):
         config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
 
-    old_trace = py.std.pdb.set_trace
+    old = (py.std.pdb.set_trace, pytestPDB._pluginmanager)
     def fin():
-        py.std.pdb.set_trace = old_trace
+        py.std.pdb.set_trace, pytestPDB._pluginmanager = old
     py.std.pdb.set_trace = pytest.set_trace
+    pytestPDB._pluginmanager = config.pluginmanager
     config._cleanup.append(fin)
 
 class pytestPDB:
     """ Pseudo PDB that defers to the real pdb. """
-    item = None
-    collector = None
+    _pluginmanager = None
 
     def set_trace(self):
         """ invoke PDB set_trace debugging, dropping any IO capturing. """
         frame = sys._getframe().f_back
-        item = self.item or self.collector
-
-        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)
+        capman = None
+        if self._pluginmanager is not None:
+            capman = self._pluginmanager.getplugin("capturemanager")
+            if capman:
+                capman.reset_capturings()
             tw = py.io.TerminalWriter()
             tw.line()
             tw.sep(">", "PDB set_trace (IO-capturing turned off)")
         py.std.pdb.Pdb().set_trace(frame)
 
-def pdbitem(item):
-    pytestPDB.item = item
-pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
-
- at pytest.mark.tryfirst
-def pytest_make_collect_report(__multicall__, collector):
-    try:
-        pytestPDB.collector = collector
-        return __multicall__.execute()
-    finally:
-        pytestPDB.collector = None
-
-def pytest_runtest_makereport():
-    pytestPDB.item = None
 
 class PdbInvoke:
     def pytest_exception_interact(self, node, call, report):
+        capman = node.config.pluginmanager.getplugin("capturemanager")
+        if capman:
+            capman.reset_capturings()
         return _enter_pdb(node, call.excinfo, report)
 
     def pytest_internalerror(self, excrepr, excinfo):

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -885,7 +885,7 @@
         nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:]))
         nodeid.replace(session.fspath.sep, "/")
 
-    tw = py.io.TerminalWriter()
+    tw = config.get_terminal_writer()
     verbose = config.getvalue("verbose")
 
     fm = session._fixturemanager

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/runner.py
--- a/_pytest/runner.py
+++ b/_pytest/runner.py
@@ -135,14 +135,13 @@
         self.when = when
         self.start = time()
         try:
-            try:
-                self.result = func()
-            except KeyboardInterrupt:
-                raise
-            except:
-                self.excinfo = py.code.ExceptionInfo()
-        finally:
+            self.result = func()
+        except KeyboardInterrupt:
             self.stop = time()
+            raise
+        except:
+            self.excinfo = py.code.ExceptionInfo()
+        self.stop = time()
 
     def __repr__(self):
         if self.excinfo:
@@ -178,6 +177,11 @@
             except UnicodeEncodeError:
                 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")
     failed = property(lambda x: x.outcome == "failed")
     skipped = property(lambda x: x.outcome == "skipped")
@@ -191,6 +195,7 @@
     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 +214,18 @@
             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
 
@@ -286,7 +293,8 @@
 
 
 class CollectReport(BaseReport):
-    def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
+    def __init__(self, nodeid, outcome, longrepr, result,
+                 sections=(), **extra):
         self.nodeid = nodeid
         self.outcome = outcome
         self.longrepr = longrepr

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -36,7 +36,10 @@
 
 def pytest_configure(config):
     config.option.verbose -= config.option.quiet
-    reporter = TerminalReporter(config, sys.stdout)
+    out = config.pluginmanager.getplugin("dupped_stdout")
+    #if out is None:
+    #    out = sys.stdout
+    reporter = TerminalReporter(config, out)
     config.pluginmanager.register(reporter, 'terminalreporter')
     if config.option.debug or config.option.traceconfig:
         def mywriter(tags, args):
@@ -44,6 +47,11 @@
             reporter.write_line("[traceconfig] " + msg)
         config.trace.root.setprocessor("pytest:config", mywriter)
 
+def get_terminal_writer(config):
+    tr = config.pluginmanager.getplugin("terminalreporter")
+    return tr._tw
+
+
 def getreportopt(config):
     reportopts = ""
     optvalue = config.option.report

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 bench/bench.py
--- a/bench/bench.py
+++ b/bench/bench.py
@@ -4,8 +4,8 @@
     import cProfile
     import pytest
     import pstats
-    script = sys.argv[1] if len(sys.argv) > 1 else "empty.py"
-    stats = cProfile.run('pytest.cmdline.main([%r])' % script, 'prof')
+    script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py"
+    stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof')
     p = pstats.Stats("prof")
     p.strip_dirs()
     p.sort_stats('cumulative')

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -4,6 +4,7 @@
 import os
 import sys
 import py
+import tempfile
 import pytest
 import contextlib
 
@@ -44,6 +45,13 @@
 
 
 
+def StdCaptureFD(out=True, err=True, in_=True):
+    return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture)
+
+def StdCapture(out=True, err=True, in_=True):
+    return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture)
+
+
 class TestCaptureManager:
     def test_getmethod_default_no_fd(self, testdir, monkeypatch):
         config = testdir.parseconfig(testdir.tmpdir)
@@ -75,7 +83,7 @@
     @needsosdup
     @pytest.mark.parametrize("method", ['no', 'fd', 'sys'])
     def test_capturing_basic_api(self, method):
-        capouter = capture.StdCaptureFD()
+        capouter = StdCaptureFD()
         old = sys.stdout, sys.stderr, sys.stdin
         try:
             capman = CaptureManager()
@@ -95,11 +103,11 @@
             assert not out and not err
             capman.reset_capturings()
         finally:
-            capouter.reset()
+            capouter.stop_capturing()
 
     @needsosdup
     def test_juggle_capturings(self, testdir):
-        capouter = capture.StdCaptureFD()
+        capouter = StdCaptureFD()
         try:
             #config = testdir.parseconfig(testdir.tmpdir)
             capman = CaptureManager()
@@ -119,7 +127,7 @@
             finally:
                 capman.reset_capturings()
         finally:
-            capouter.reset()
+            capouter.stop_capturing()
 
 
 @pytest.mark.parametrize("method", ['fd', 'sys'])
@@ -282,9 +290,9 @@
             "====* FAILURES *====",
             "____*____",
             "*test_capturing_outerr.py:8: ValueError",
-            "*--- Captured stdout ---*",
+            "*--- Captured stdout *call*",
             "1",
-            "*--- Captured stderr ---*",
+            "*--- Captured stderr *call*",
             "2",
         ])
 
@@ -688,17 +696,15 @@
         cap = capture.FDCapture(fd)
         data = tobytes("hello")
         os.write(fd, data)
-        f = cap.done()
-        s = f.read()
-        f.close()
+        s = cap.snap()
+        cap.done()
         assert not s
         cap = capture.FDCapture(fd)
         cap.start()
         os.write(fd, data)
-        f = cap.done()
-        s = f.read()
+        s = cap.snap()
+        cap.done()
         assert s == "hello"
-        f.close()
 
     def test_simple_many(self, tmpfile):
         for i in range(10):
@@ -712,22 +718,21 @@
     def test_simple_fail_second_start(self, tmpfile):
         fd = tmpfile.fileno()
         cap = capture.FDCapture(fd)
-        f = cap.done()
+        cap.done()
         pytest.raises(ValueError, cap.start)
-        f.close()
 
     def test_stderr(self):
-        cap = capture.FDCapture(2, patchsys=True)
+        cap = capture.FDCapture(2)
         cap.start()
         print_("hello", file=sys.stderr)
-        f = cap.done()
-        s = f.read()
+        s = cap.snap()
+        cap.done()
         assert s == "hello\n"
 
     def test_stdin(self, tmpfile):
         tmpfile.write(tobytes("3"))
         tmpfile.seek(0)
-        cap = capture.FDCapture(0, tmpfile=tmpfile)
+        cap = capture.FDCapture(0, tmpfile)
         cap.start()
         # check with os.read() directly instead of raw_input(), because
         # sys.stdin itself may be redirected (as pytest now does by default)
@@ -744,123 +749,121 @@
             cap.writeorg(data2)
         finally:
             tmpfile.close()
-        f = cap.done()
-        scap = f.read()
+        scap = cap.snap()
+        cap.done()
         assert scap == totext(data1)
         stmp = open(tmpfile.name, 'rb').read()
         assert stmp == data2
 
 
 class TestStdCapture:
+    captureclass = staticmethod(StdCapture)
+
+    @contextlib.contextmanager
     def getcapture(self, **kw):
-        cap = capture.StdCapture(**kw)
-        cap.startall()
-        return cap
+        cap = self.__class__.captureclass(**kw)
+        cap.start_capturing()
+        try:
+            yield cap
+        finally:
+            cap.stop_capturing()
 
     def test_capturing_done_simple(self):
-        cap = self.getcapture()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        outfile, errfile = cap.done()
-        s = outfile.read()
-        assert s == "hello"
-        s = errfile.read()
-        assert s == "world"
+        with self.getcapture() as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
+        assert out == "hello"
+        assert err == "world"
 
     def test_capturing_reset_simple(self):
-        cap = self.getcapture()
-        print("hello world")
-        sys.stderr.write("hello error\n")
-        out, err = cap.reset()
+        with self.getcapture() as cap:
+            print("hello world")
+            sys.stderr.write("hello error\n")
+            out, err = cap.readouterr()
         assert out == "hello world\n"
         assert err == "hello error\n"
 
     def test_capturing_readouterr(self):
-        cap = self.getcapture()
-        try:
+        with self.getcapture() as cap:
             print ("hello world")
             sys.stderr.write("hello error\n")
             out, err = cap.readouterr()
             assert out == "hello world\n"
             assert err == "hello error\n"
             sys.stderr.write("error2")
-        finally:
-            out, err = cap.reset()
+            out, err = cap.readouterr()
         assert err == "error2"
 
     def test_capturing_readouterr_unicode(self):
-        cap = self.getcapture()
-        try:
+        with self.getcapture() as cap:
             print ("hx\xc4\x85\xc4\x87")
             out, err = cap.readouterr()
-        finally:
-            cap.reset()
         assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
 
     @pytest.mark.skipif('sys.version_info >= (3,)',
                         reason='text output different for bytes on python3')
     def test_capturing_readouterr_decode_error_handling(self):
-        cap = self.getcapture()
-        # triggered a internal error in pytest
-        print('\xa6')
-        out, err = cap.readouterr()
+        with self.getcapture() as cap:
+            # triggered a internal error in pytest
+            print('\xa6')
+            out, err = cap.readouterr()
         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
 
     def test_reset_twice_error(self):
-        cap = self.getcapture()
-        print ("hello")
-        out, err = cap.reset()
-        pytest.raises(ValueError, cap.reset)
+        with self.getcapture() as cap:
+            print ("hello")
+            out, err = cap.readouterr()
+        pytest.raises(ValueError, cap.stop_capturing)
         assert out == "hello\n"
         assert not err
 
     def test_capturing_modify_sysouterr_in_between(self):
         oldout = sys.stdout
         olderr = sys.stderr
-        cap = self.getcapture()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        sys.stdout = capture.TextIO()
-        sys.stderr = capture.TextIO()
-        print ("not seen")
-        sys.stderr.write("not seen\n")
-        out, err = cap.reset()
+        with self.getcapture() as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            sys.stdout = capture.TextIO()
+            sys.stderr = capture.TextIO()
+            print ("not seen")
+            sys.stderr.write("not seen\n")
+            out, err = cap.readouterr()
         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()
-        out1, err1 = cap1.reset()
+        with self.getcapture() as cap1:
+            print ("cap1")
+            with self.getcapture() as cap2:
+                print ("cap2")
+                out2, err2 = cap2.readouterr()
+                out1, err1 = cap1.readouterr()
         assert out1 == "cap1\n"
         assert out2 == "cap2\n"
 
     def test_just_out_capture(self):
-        cap = self.getcapture(out=True, err=False)
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        out, err = cap.reset()
+        with self.getcapture(out=True, err=False) as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
         assert out == "hello"
         assert not err
 
     def test_just_err_capture(self):
-        cap = self.getcapture(out=False, err=True)
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        out, err = cap.reset()
+        with self.getcapture(out=False, err=True) as cap:
+            sys.stdout.write("hello")
+            sys.stderr.write("world")
+            out, err = cap.readouterr()
         assert err == "world"
         assert not out
 
     def test_stdin_restored(self):
         old = sys.stdin
-        cap = self.getcapture(in_=True)
-        newstdin = sys.stdin
-        out, err = cap.reset()
+        with self.getcapture(in_=True) as cap:
+            newstdin = sys.stdin
         assert newstdin != sys.stdin
         assert sys.stdin is old
 
@@ -868,68 +871,47 @@
         print ("XXX this test may well hang instead of crashing")
         print ("XXX which indicates an error in the underlying capturing")
         print ("XXX mechanisms")
-        cap = self.getcapture()
-        pytest.raises(IOError, "sys.stdin.read()")
-        out, err = cap.reset()
-
-    def test_suspend_resume(self):
-        cap = self.getcapture(out=True, err=False, in_=False)
-        try:
-            print ("hello")
-            sys.stderr.write("error\n")
-            out, err = cap.suspend()
-            assert out == "hello\n"
-            assert not err
-            print ("in between")
-            sys.stderr.write("in between\n")
-            cap.resume()
-            print ("after")
-            sys.stderr.write("error_after\n")
-        finally:
-            out, err = cap.reset()
-        assert out == "after\n"
-        assert not err
+        with self.getcapture() as cap:
+            pytest.raises(IOError, "sys.stdin.read()")
 
 
 class TestStdCaptureFD(TestStdCapture):
     pytestmark = needsosdup
+    captureclass = staticmethod(StdCaptureFD)
 
-    def getcapture(self, **kw):
-        cap = capture.StdCaptureFD(**kw)
-        cap.startall()
-        return cap
+    def test_simple_only_fd(self, testdir):
+        testdir.makepyfile("""
+            import os
+            def test_x():
+                os.write(1, "hello\\n".encode("ascii"))
+                assert 0
+        """)
+        result = testdir.runpytest()
+        result.stdout.fnmatch_lines("""
+            *test_x*
+            *assert 0*
+            *Captured stdout*
+        """)
 
     def test_intermingling(self):
-        cap = self.getcapture()
-        oswritebytes(1, "1")
-        sys.stdout.write(str(2))
-        sys.stdout.flush()
-        oswritebytes(1, "3")
-        oswritebytes(2, "a")
-        sys.stderr.write("b")
-        sys.stderr.flush()
-        oswritebytes(2, "c")
-        out, err = cap.reset()
+        with self.getcapture() as cap:
+            oswritebytes(1, "1")
+            sys.stdout.write(str(2))
+            sys.stdout.flush()
+            oswritebytes(1, "3")
+            oswritebytes(2, "a")
+            sys.stderr.write("b")
+            sys.stderr.flush()
+            oswritebytes(2, "c")
+            out, err = cap.readouterr()
         assert out == "123"
         assert err == "abc"
 
     def test_many(self, capfd):
         with lsof_check():
             for i in range(10):
-                cap = capture.StdCaptureFD()
-                cap.reset()
-
-
- at needsosdup
-def test_stdcapture_fd_tmpfile(tmpfile):
-    capfd = capture.StdCaptureFD(out=tmpfile)
-    try:
-        os.write(1, "hello".encode("ascii"))
-        os.write(2, "world".encode("ascii"))
-        outf, errf = capfd.done()
-    finally:
-        capfd.reset()
-    assert outf == tmpfile
+                cap = StdCaptureFD()
+                cap.stop_capturing()
 
 
 class TestStdCaptureFDinvalidFD:
@@ -938,19 +920,22 @@
     def test_stdcapture_fd_invalid_fd(self, testdir):
         testdir.makepyfile("""
             import os
-            from _pytest.capture import StdCaptureFD
+            from _pytest import capture
+            def StdCaptureFD(out=True, err=True, in_=True):
+                return capture.StdCaptureBase(out, err, in_,
+                                              Capture=capture.FDCapture)
             def test_stdout():
                 os.close(1)
                 cap = StdCaptureFD(out=True, err=False, in_=False)
-                cap.done()
+                cap.stop_capturing()
             def test_stderr():
                 os.close(2)
                 cap = StdCaptureFD(out=False, err=True, in_=False)
-                cap.done()
+                cap.stop_capturing()
             def test_stdin():
                 os.close(0)
                 cap = StdCaptureFD(out=False, err=False, in_=True)
-                cap.done()
+                cap.stop_capturing()
         """)
         result = testdir.runpytest("--capture=fd")
         assert result.ret == 0
@@ -958,27 +943,8 @@
 
 
 def test_capture_not_started_but_reset():
-    capsys = capture.StdCapture()
-    capsys.done()
-    capsys.done()
-    capsys.reset()
-
-
- at needsosdup
-def test_capture_no_sys():
-    capsys = capture.StdCapture()
-    try:
-        cap = capture.StdCaptureFD(patchsys=False)
-        cap.startall()
-        sys.stdout.write("hello")
-        sys.stderr.write("world")
-        oswritebytes(1, "1")
-        oswritebytes(2, "2")
-        out, err = cap.reset()
-        assert out == "1"
-        assert err == "2"
-    finally:
-        capsys.reset()
+    capsys = StdCapture()
+    capsys.stop_capturing()
 
 
 @needsosdup
@@ -986,19 +952,18 @@
 def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
     if not use:
         tmpfile = True
-    cap = capture.StdCaptureFD(out=False, err=tmpfile)
+    cap = StdCaptureFD(out=False, err=tmpfile)
     try:
-        cap.startall()
+        cap.start_capturing()
         capfile = cap.err.tmpfile
-        cap.suspend()
-        cap.resume()
+        cap.readouterr()
     finally:
-        cap.reset()
+        cap.stop_capturing()
     capfile2 = cap.err.tmpfile
     assert capfile2 == capfile
 
 
- at pytest.mark.parametrize('method', ['StdCapture', 'StdCaptureFD'])
+ at pytest.mark.parametrize('method', ['SysCapture', 'FDCapture'])
 def test_capturing_and_logging_fundamentals(testdir, method):
     if method == "StdCaptureFD" and not hasattr(os, 'dup'):
         pytest.skip("need os.dup")
@@ -1007,23 +972,27 @@
         import sys, os
         import py, logging
         from _pytest import capture
-        cap = capture.%s(out=False, in_=False)
-        cap.startall()
+        cap = capture.StdCaptureBase(out=False, in_=False,
+                                     Capture=capture.%s)
+        cap.start_capturing()
 
         logging.warn("hello1")
-        outerr = cap.suspend()
+        outerr = cap.readouterr()
         print ("suspend, captured %%s" %%(outerr,))
         logging.warn("hello2")
 
-        cap.resume()
+        cap.pop_outerr_to_orig()
         logging.warn("hello3")
 
-        outerr = cap.suspend()
+        outerr = cap.readouterr()
         print ("suspend2, captured %%s" %% (outerr,))
     """ % (method,))
     result = testdir.runpython(p)
-    result.stdout.fnmatch_lines([
-        "suspend, captured*hello1*",
-        "suspend2, captured*hello2*WARNING:root:hello3*",
-    ])
+    result.stdout.fnmatch_lines("""
+        suspend, captured*hello1*
+        suspend2, captured*WARNING:root:hello3*
+    """)
+    result.stderr.fnmatch_lines("""
+        WARNING:root:hello2
+    """)
     assert "atexit" not in result.stderr.str()

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_core.py
--- a/testing/test_core.py
+++ b/testing/test_core.py
@@ -523,6 +523,95 @@
         res = MultiCall([m1, m2], {}).execute()
         assert res == [1]
 
+    def test_hookwrapper(self):
+        l = []
+        def m1():
+            l.append("m1 init")
+            yield None
+            l.append("m1 finish")
+        m1.hookwrapper = True
+
+        def m2():
+            l.append("m2")
+            return 2
+        res = MultiCall([m2, m1], {}).execute()
+        assert res == [2]
+        assert l == ["m1 init", "m2", "m1 finish"]
+        l[:] = []
+        res = MultiCall([m2, m1], {}, firstresult=True).execute()
+        assert res == 2
+        assert l == ["m1 init", "m2", "m1 finish"]
+
+    def test_hookwrapper_order(self):
+        l = []
+        def m1():
+            l.append("m1 init")
+            yield 1
+            l.append("m1 finish")
+        m1.hookwrapper = True
+
+        def m2():
+            l.append("m2 init")
+            yield 2
+            l.append("m2 finish")
+        m2.hookwrapper = True
+        res = MultiCall([m2, m1], {}).execute()
+        assert res == [1, 2]
+        assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"]
+
+    def test_listattr_hookwrapper_ordering(self):
+        class P1:
+            @pytest.mark.hookwrapper
+            def m(self):
+                return 17
+
+        class P2:
+            def m(self):
+                return 23
+
+        class P3:
+            @pytest.mark.tryfirst
+            def m(self):
+                return 19
+
+        pluginmanager = PluginManager()
+        p1 = P1()
+        p2 = P2()
+        p3 = P3()
+        pluginmanager.register(p1)
+        pluginmanager.register(p2)
+        pluginmanager.register(p3)
+        methods = pluginmanager.listattr('m')
+        assert methods == [p2.m, p3.m, p1.m]
+        ## listattr keeps a cache and deleting
+        ## a function attribute requires clearing it
+        #pluginmanager._listattrcache.clear()
+        #del P1.m.__dict__['tryfirst']
+
+    def test_hookwrapper_not_yield(self):
+        def m1():
+            pass
+        m1.hookwrapper = True
+
+        mc = MultiCall([m1], {})
+        with pytest.raises(mc.WrongHookWrapper) as ex:
+            mc.execute()
+        assert ex.value.func == m1
+        assert ex.value.message
+
+    def test_hookwrapper_too_many_yield(self):
+        def m1():
+            yield 1
+            yield 2
+        m1.hookwrapper = True
+
+        mc = MultiCall([m1], {})
+        with pytest.raises(mc.WrongHookWrapper) as ex:
+            mc.execute()
+        assert ex.value.func == m1
+        assert ex.value.message
+
+
 class TestHookRelay:
     def test_happypath(self):
         pm = PluginManager()

diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_junitxml.py
--- a/testing/test_junitxml.py
+++ b/testing/test_junitxml.py
@@ -478,10 +478,12 @@
     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()


https://bitbucket.org/hpk42/pytest/commits/deee08c4eef3/
Changeset:   deee08c4eef3
User:        hpk42
Date:        2014-03-28 09:46:38
Summary:     cleanup and refine issue412 test (still failing on py33)
Affected #:  2 files

diff -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -12,30 +12,7 @@
 import py
 import pytest
 
-try:
-    from io import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-try:
-    from io import BytesIO
-except ImportError:
-    class BytesIO(StringIO):
-        def write(self, data):
-            if isinstance(data, unicode):
-                raise TypeError("not a byte value: %r" % (data,))
-            StringIO.write(self, data)
-
-if sys.version_info < (3, 0):
-    class TextIO(StringIO):
-        def write(self, data):
-            if not isinstance(data, unicode):
-                enc = getattr(self, '_encoding', 'UTF-8')
-                data = unicode(data, enc, 'replace')
-            StringIO.write(self, data)
-else:
-    TextIO = StringIO
-
+from py.io import TextIO
 
 patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
 
@@ -232,7 +209,8 @@
 error_capsysfderror = "cannot use capsys and capfd at the same time"
 
 
-def pytest_funcarg__capsys(request):
+ at pytest.fixture
+def capsys(request):
     """enables capturing of writes to sys.stdout/sys.stderr and makes
     captured output available via ``capsys.readouterr()`` method calls
     which return a ``(out, err)`` tuple.
@@ -242,7 +220,8 @@
     request.node._capfuncarg = c = CaptureFixture(SysCapture)
     return c
 
-def pytest_funcarg__capfd(request):
+ at pytest.fixture
+def capfd(request):
     """enables capturing of writes to file descriptors 1 and 2 and makes
     captured output available via ``capsys.readouterr()`` method calls
     which return a ``(out, err)`` tuple.

diff -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -541,8 +541,8 @@
     assert 'hello19' not in result.stdout.str()
 
 
- at pytest.mark.xfail(sys.version_info>=(3,), reason='demonstrate #412')
-def test_capture_badoutput(testdir):
+ at pytest.mark.xfail("sys.version_info >= (3,)")
+def test_capture_badoutput_issue412(testdir):
     testdir.makepyfile("""
         import os
 
@@ -552,11 +552,12 @@
             assert 0
         """)
     result = testdir.runpytest('--cap=fd')
-    #this fails on  python3 - fnmatch first for debugging
-    result.stdout.fnmatch_lines([
-        '*1 failed*',
-        ])
-    assert result.ret == 1
+    result.stdout.fnmatch_lines('''
+        *def test_func*
+        *assert 0*
+        *Captured*
+        *1 failed*
+    ''')
 
 
 def test_capture_early_option_parsing(testdir):
@@ -616,7 +617,7 @@
 
 
 def test_bytes_io():
-    f = capture.BytesIO()
+    f = py.io.BytesIO()
     f.write(tobytes("hello"))
     pytest.raises(TypeError, "f.write(totext('hello'))")
     s = f.getvalue()


https://bitbucket.org/hpk42/pytest/commits/116ac00cd354/
Changeset:   116ac00cd354
User:        hpk42
Date:        2014-03-28 11:27:02
Summary:     fix issue412 and other encoding issues.  Streamline dupfile() into
a new more thoughtful safe_text_dupfile helper.
Affected #:  3 files

diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -13,6 +13,7 @@
 import pytest
 
 from py.io import TextIO
+unicode = py.builtin.text
 
 patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
 
@@ -38,12 +39,7 @@
         method = "sys"
     pluginmanager = early_config.pluginmanager
     if method != "no":
-        try:
-            sys.stdout.fileno()
-        except Exception:
-            dupped_stdout = sys.stdout
-        else:
-            dupped_stdout = dupfile(sys.stdout, buffering=1)
+        dupped_stdout = safe_text_dupfile(sys.stdout, "wb")
         pluginmanager.register(dupped_stdout, "dupped_stdout")
             #pluginmanager.add_shutdown(dupped_stdout.close)
     capman = CaptureManager(method)
@@ -256,51 +252,41 @@
             return "", ""
 
 
-def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
-    """ return a new open file object that's a duplicate of f
-
-        mode is duplicated if not given, 'buffering' controls
-        buffer size (defaulting to no buffering) and 'raising'
-        defines whether an exception is raised when an incompatible
-        file object is passed in (if raising is False, the file
-        object itself will be returned)
+def safe_text_dupfile(f, mode, default_encoding="UTF8"):
+    """ return a open text file object that's a duplicate of f on the
+        FD-level if possible.
     """
+    encoding = getattr(f, "encoding", None)
     try:
         fd = f.fileno()
-        mode = mode or f.mode
-    except AttributeError:
-        if raising:
-            raise
-        return f
-    newfd = os.dup(fd)
-    if sys.version_info >= (3, 0):
-        if encoding is not None:
-            mode = mode.replace("b", "")
-            buffering = True
-        return os.fdopen(newfd, mode, buffering, encoding, closefd=True)
+    except Exception:
+        if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"):
+            # we seem to have a text stream, let's just use it
+            return f
     else:
-        f = os.fdopen(newfd, mode, buffering)
-        if encoding is not None:
-            return EncodedFile(f, encoding)
-        return f
+        newfd = os.dup(fd)
+        if "b" not in mode:
+            mode += "b"
+        f = os.fdopen(newfd, mode, 0)  # no buffering
+    return EncodedFile(f, encoding or default_encoding)
 
 
 class EncodedFile(object):
-    def __init__(self, _stream, encoding):
-        self._stream = _stream
+    def __init__(self, buffer, encoding):
+        self.buffer = buffer
         self.encoding = encoding
 
     def write(self, obj):
         if isinstance(obj, unicode):
-            obj = obj.encode(self.encoding)
-        self._stream.write(obj)
+            obj = obj.encode(self.encoding, "replace")
+        self.buffer.write(obj)
 
     def writelines(self, linelist):
         data = ''.join(linelist)
         self.write(data)
 
     def __getattr__(self, name):
-        return getattr(self._stream, name)
+        return getattr(self.buffer, name)
 
 
 class StdCaptureBase(object):
@@ -344,13 +330,8 @@
 
     def readouterr(self):
         """ return snapshot unicode value of stdout/stderr capturings. """
-        return self._readsnapshot('out'), self._readsnapshot('err')
-
-    def _readsnapshot(self, name):
-        cap = getattr(self, name, None)
-        if cap is None:
-            return ""
-        return cap.snap()
+        return (self.out.snap() if self.out is not None else "",
+                self.err.snap() if self.err is not None else "")
 
 
 class FDCapture:
@@ -370,7 +351,7 @@
                 else:
                     f = TemporaryFile()
                     with f:
-                        tmpfile = dupfile(f, encoding="UTF-8")
+                        tmpfile = safe_text_dupfile(f, mode="wb+")
             self.tmpfile = tmpfile
             if targetfd in patchsysdict:
                 self._oldsys = getattr(sys, patchsysdict[targetfd])

diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -104,6 +104,7 @@
         self.startdir = self.curdir = py.path.local()
         if file is None:
             file = py.std.sys.stdout
+        #assert file.encoding
         self._tw = self.writer = py.io.TerminalWriter(file)
         if self.config.option.color == 'yes':
             self._tw.hasmarkup = True
@@ -144,7 +145,8 @@
         self._tw.write(content, **markup)
 
     def write_line(self, line, **markup):
-        line = str(line)
+        if not py.builtin._istext(line):
+            line = py.builtin.text(line, errors="replace")
         self.ensure_newline()
         self._tw.line(line, **markup)
 
@@ -163,7 +165,7 @@
         self._tw.line(msg, **kw)
 
     def pytest_internalerror(self, excrepr):
-        for line in str(excrepr).split("\n"):
+        for line in py.builtin.text(excrepr).split("\n"):
             self.write_line("INTERNALERROR> " + line)
         return 1
 

diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -541,7 +541,6 @@
     assert 'hello19' not in result.stdout.str()
 
 
- at pytest.mark.xfail("sys.version_info >= (3,)")
 def test_capture_badoutput_issue412(testdir):
     testdir.makepyfile("""
         import os
@@ -571,8 +570,6 @@
     assert 'hello19' in result.stdout.str()
 
 
- at pytest.mark.xfail(sys.version_info >= (3, 0), reason='encoding issues')
- at pytest.mark.xfail(sys.version_info < (2, 6), reason='test not run on py25')
 def test_capture_binary_output(testdir):
     testdir.makepyfile(r"""
         import pytest
@@ -646,7 +643,7 @@
 def test_dupfile(tmpfile):
     flist = []
     for i in range(5):
-        nf = capture.dupfile(tmpfile, encoding="utf-8")
+        nf = capture.safe_text_dupfile(tmpfile, "wb")
         assert nf != tmpfile
         assert nf.fileno() != tmpfile.fileno()
         assert nf not in flist
@@ -660,19 +657,17 @@
     assert "01234" in repr(s)
     tmpfile.close()
 
+def test_dupfile_on_bytesio():
+    io = py.io.BytesIO()
+    f = capture.safe_text_dupfile(io, "wb")
+    f.write("hello")
+    assert io.getvalue() == b"hello"
 
-def test_dupfile_no_mode():
-    """
-    dupfile should trap an AttributeError and return f if no mode is supplied.
-    """
-    class SomeFileWrapper(object):
-        "An object with a fileno method but no mode attribute"
-        def fileno(self):
-            return 1
-    tmpfile = SomeFileWrapper()
-    assert capture.dupfile(tmpfile) is tmpfile
-    with pytest.raises(AttributeError):
-        capture.dupfile(tmpfile, raising=True)
+def test_dupfile_on_textio():
+    io = py.io.TextIO()
+    f = capture.safe_text_dupfile(io, "wb")
+    f.write("hello")
+    assert io.getvalue() == "hello"
 
 
 @contextlib.contextmanager


https://bitbucket.org/hpk42/pytest/commits/8004c01ca861/
Changeset:   8004c01ca861
User:        hpk42
Date:        2014-03-28 11:29:29
Summary:     add changelog entry:
fix issue412: messing with stdout/stderr FD-level streams is now
captured without crashes.
Affected #:  1 file

diff -r 116ac00cd354f38cd0d55118581c31c1cb845bee -r 8004c01ca861a7e1bd0a7939730e9660b59242cf CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -23,6 +23,9 @@
 
 - merge PR123: improved integration with mock.patch decorator on tests.
 
+- fix issue412: messing with stdout/stderr FD-level streams is now
+  captured without crashes.
+
 
 2.5.2
 -----------------------------------


https://bitbucket.org/hpk42/pytest/commits/529b5e415bdb/
Changeset:   529b5e415bdb
User:        hpk42
Date:        2014-04-01 14:19:52
Summary:     rename StdCaptureBase to MultiCapture
Affected #:  2 files

diff -r 8004c01ca861a7e1bd0a7939730e9660b59242cf -r 529b5e415bdbbc441e83653c0a0143e22398768b _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -81,11 +81,11 @@
 
     def _getcapture(self, method):
         if method == "fd":
-            return StdCaptureBase(out=True, err=True, Capture=FDCapture)
+            return MultiCapture(out=True, err=True, Capture=FDCapture)
         elif method == "sys":
-            return StdCaptureBase(out=True, err=True, Capture=SysCapture)
+            return MultiCapture(out=True, err=True, Capture=SysCapture)
         elif method == "no":
-            return StdCaptureBase(out=False, err=False, in_=False)
+            return MultiCapture(out=False, err=False, in_=False)
         else:
             raise ValueError("unknown capturing method: %r" % method)
 
@@ -235,7 +235,7 @@
         self.captureclass = captureclass
 
     def _start(self):
-        self._capture = StdCaptureBase(out=True, err=True, in_=False,
+        self._capture = MultiCapture(out=True, err=True, in_=False,
                                        Capture=self.captureclass)
         self._capture.start_capturing()
 
@@ -289,7 +289,7 @@
         return getattr(self.buffer, name)
 
 
-class StdCaptureBase(object):
+class MultiCapture(object):
     out = err = in_ = None
 
     def __init__(self, out=True, err=True, in_=True, Capture=None):

diff -r 8004c01ca861a7e1bd0a7939730e9660b59242cf -r 529b5e415bdbbc441e83653c0a0143e22398768b testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -46,10 +46,10 @@
 
 
 def StdCaptureFD(out=True, err=True, in_=True):
-    return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture)
+    return capture.MultiCapture(out, err, in_, Capture=capture.FDCapture)
 
 def StdCapture(out=True, err=True, in_=True):
-    return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture)
+    return capture.MultiCapture(out, err, in_, Capture=capture.SysCapture)
 
 
 class TestCaptureManager:
@@ -918,7 +918,7 @@
             import os
             from _pytest import capture
             def StdCaptureFD(out=True, err=True, in_=True):
-                return capture.StdCaptureBase(out, err, in_,
+                return capture.MultiCapture(out, err, in_,
                                               Capture=capture.FDCapture)
             def test_stdout():
                 os.close(1)
@@ -958,6 +958,25 @@
     capfile2 = cap.err.tmpfile
     assert capfile2 == capfile
 
+ at needsosdup
+def test_close_and_capture_again(testdir):
+    testdir.makepyfile("""
+        import os
+        def test_close():
+            os.close(1)
+        def test_capture_again():
+            os.write(1, "hello\\n")
+            assert 0
+    """)
+    result = testdir.runpytest()
+    result.stdout.fnmatch_lines("""
+        *test_capture_again*
+        *assert 0*
+        *stdout*
+        *hello*
+    """)
+
+
 
 @pytest.mark.parametrize('method', ['SysCapture', 'FDCapture'])
 def test_capturing_and_logging_fundamentals(testdir, method):
@@ -968,7 +987,7 @@
         import sys, os
         import py, logging
         from _pytest import capture
-        cap = capture.StdCaptureBase(out=False, in_=False,
+        cap = capture.MultiCapture(out=False, in_=False,
                                      Capture=capture.%s)
         cap.start_capturing()
 


https://bitbucket.org/hpk42/pytest/commits/cf556c9a4df3/
Changeset:   cf556c9a4df3
User:        hpk42
Date:        2014-04-01 14:19:55
Summary:     avoid some redundancy by using SysCapture from FDCapture for manipulating sys.std{out,in,err}
Affected #:  2 files

diff -r 529b5e415bdbbc441e83653c0a0143e22398768b -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -333,6 +333,8 @@
         return (self.out.snap() if self.out is not None else "",
                 self.err.snap() if self.err is not None else "")
 
+class NoCapture:
+    __init__ = start = done = lambda *args: None
 
 class FDCapture:
     """ Capture IO to/from a given os-level filedescriptor. """
@@ -340,36 +342,38 @@
     def __init__(self, targetfd, tmpfile=None):
         self.targetfd = targetfd
         try:
-            self._savefd = os.dup(self.targetfd)
+            self.targetfd_save = os.dup(self.targetfd)
         except OSError:
             self.start = lambda: None
             self.done = lambda: None
         else:
-            if tmpfile is None:
-                if targetfd == 0:
-                    tmpfile = open(os.devnull, "r")
-                else:
+            if targetfd == 0:
+                assert not tmpfile, "cannot set tmpfile with stdin"
+                tmpfile = open(os.devnull, "r")
+                self.syscapture = SysCapture(targetfd)
+            else:
+                if tmpfile is None:
                     f = TemporaryFile()
                     with f:
                         tmpfile = safe_text_dupfile(f, mode="wb+")
+                if targetfd in patchsysdict:
+                    self.syscapture = SysCapture(targetfd, tmpfile)
+                else:
+                    self.syscapture = NoCapture()
             self.tmpfile = tmpfile
-            if targetfd in patchsysdict:
-                self._oldsys = getattr(sys, patchsysdict[targetfd])
+            self.tmpfile_fd = tmpfile.fileno()
 
     def __repr__(self):
-        return "<FDCapture %s oldfd=%s>" % (self.targetfd, self._savefd)
+        return "<FDCapture %s oldfd=%s>" % (self.targetfd, self.targetfd_save)
 
     def start(self):
         """ Start capturing on targetfd using memorized tmpfile. """
         try:
-            os.fstat(self._savefd)
-        except OSError:
+            os.fstat(self.targetfd_save)
+        except (AttributeError, OSError):
             raise ValueError("saved filedescriptor not valid anymore")
-        targetfd = self.targetfd
-        os.dup2(self.tmpfile.fileno(), targetfd)
-        if hasattr(self, '_oldsys'):
-            subst = self.tmpfile if targetfd != 0 else DontReadFromInput()
-            setattr(sys, patchsysdict[targetfd], subst)
+        os.dup2(self.tmpfile_fd, self.targetfd)
+        self.syscapture.start()
 
     def snap(self):
         f = self.tmpfile
@@ -386,28 +390,38 @@
     def done(self):
         """ stop capturing, restore streams, return original capture file,
         seeked to position zero. """
-        os.dup2(self._savefd, self.targetfd)
-        os.close(self._savefd)
-        if hasattr(self, '_oldsys'):
-            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
+        targetfd_save = self.__dict__.pop("targetfd_save")
+        os.dup2(targetfd_save, self.targetfd)
+        os.close(targetfd_save)
+        self.syscapture.done()
         self.tmpfile.close()
 
+    def suspend(self):
+        self.syscapture.suspend()
+        os.dup2(self.targetfd_save, self.targetfd)
+
+    def resume(self):
+        self.syscapture.resume()
+        os.dup2(self.tmpfile_fd, self.targetfd)
+
     def writeorg(self, data):
         """ write to original file descriptor. """
         if py.builtin._istext(data):
             data = data.encode("utf8") # XXX use encoding of original stream
-        os.write(self._savefd, data)
+        os.write(self.targetfd_save, data)
 
 
 class SysCapture:
-    def __init__(self, fd):
+    def __init__(self, fd, tmpfile=None):
         name = patchsysdict[fd]
         self._old = getattr(sys, name)
         self.name = name
-        if name == "stdin":
-            self.tmpfile = DontReadFromInput()
-        else:
-            self.tmpfile = TextIO()
+        if tmpfile is None:
+            if name == "stdin":
+                tmpfile = DontReadFromInput()
+            else:
+                tmpfile = TextIO()
+        self.tmpfile = tmpfile
 
     def start(self):
         setattr(sys, self.name, self.tmpfile)
@@ -421,8 +435,15 @@
 
     def done(self):
         setattr(sys, self.name, self._old)
+        del self._old
         self.tmpfile.close()
 
+    def suspend(self):
+        setattr(sys, self.name, self._old)
+
+    def resume(self):
+        setattr(sys, self.name, self.tmpfile)
+
     def writeorg(self, data):
         self._old.write(data)
         self._old.flush()

diff -r 529b5e415bdbbc441e83653c0a0143e22398768b -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -726,15 +726,11 @@
         assert s == "hello\n"
 
     def test_stdin(self, tmpfile):
-        tmpfile.write(tobytes("3"))
-        tmpfile.seek(0)
-        cap = capture.FDCapture(0, tmpfile)
+        cap = capture.FDCapture(0)
         cap.start()
-        # check with os.read() directly instead of raw_input(), because
-        # sys.stdin itself may be redirected (as pytest now does by default)
         x = os.read(0, 100).strip()
         cap.done()
-        assert x == tobytes("3")
+        assert x == tobytes('')
 
     def test_writeorg(self, tmpfile):
         data1, data2 = tobytes("foo"), tobytes("bar")
@@ -751,7 +747,37 @@
         stmp = open(tmpfile.name, 'rb').read()
         assert stmp == data2
 
+    def test_simple_resume_suspend(self, tmpfile):
+        with saved_fd(1):
+            cap = capture.FDCapture(1)
+            cap.start()
+            data = tobytes("hello")
+            os.write(1, data)
+            sys.stdout.write("whatever")
+            s = cap.snap()
+            assert s == "hellowhatever"
+            cap.suspend()
+            os.write(1, tobytes("world"))
+            sys.stdout.write("qlwkej")
+            assert not cap.snap()
+            cap.resume()
+            os.write(1, tobytes("but now"))
+            sys.stdout.write(" yes\n")
+            s = cap.snap()
+            assert s == "but now yes\n"
+            cap.suspend()
+            cap.done()
+            pytest.raises(AttributeError, cap.suspend)
 
+ at contextlib.contextmanager
+def saved_fd(fd):
+    new_fd = os.dup(fd)
+    try:
+        yield
+    finally:
+        os.dup2(new_fd, fd)
+
+    
 class TestStdCapture:
     captureclass = staticmethod(StdCapture)
 


https://bitbucket.org/hpk42/pytest/commits/cccaaa5ff07f/
Changeset:   cccaaa5ff07f
User:        hpk42
Date:        2014-04-01 14:19:58
Summary:     introduce resume/suspend functionality for FDCapture and SysCapture,
fixing problems with early bailouts (from argparse's parse() function e.g.)
that wrote to stdout.
Affected #:  2 files

diff -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -46,13 +46,7 @@
     pluginmanager.register(capman, "capturemanager")
 
     # make sure that capturemanager is properly reset at final shutdown
-    def teardown():
-        try:
-            capman.reset_capturings()
-        except ValueError:
-            pass
-
-    pluginmanager.add_shutdown(teardown)
+    pluginmanager.add_shutdown(capman.reset_capturings)
 
     # make sure logging does not raise exceptions at the end
     def silence_logging_at_shutdown():
@@ -124,16 +118,18 @@
             self._method2capture[method] = cap = self._getcapture(method)
             cap.start_capturing()
         else:
-            cap.pop_outerr_to_orig()
+            cap.resume_capturing()
 
     def suspendcapture(self, item=None):
         self.deactivate_funcargs()
         method = self.__dict__.pop("_capturing", None)
+        outerr = "", ""
         if method is not None:
             cap = self._method2capture.get(method)
             if cap is not None:
-                return cap.readouterr()
-        return "", ""
+                outerr = cap.readouterr()
+                cap.suspend_capturing()
+        return outerr
 
     def activate_funcargs(self, pyfuncitem):
         capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
@@ -316,6 +312,18 @@
         if err:
             self.err.writeorg(err)
 
+    def suspend_capturing(self):
+        if self.out:
+            self.out.suspend()
+        if self.err:
+            self.err.suspend()
+
+    def resume_capturing(self):
+        if self.out:
+            self.out.resume()
+        if self.err:
+            self.err.resume()
+
     def stop_capturing(self):
         """ stop capturing and reset capturing streams """
         if hasattr(self, '_reset'):
@@ -334,7 +342,7 @@
                 self.err.snap() if self.err is not None else "")
 
 class NoCapture:
-    __init__ = start = done = lambda *args: None
+    __init__ = start = done = suspend = resume = lambda *args: None
 
 class FDCapture:
     """ Capture IO to/from a given os-level filedescriptor. """

diff -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc testing/acceptance_test.py
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -335,6 +335,12 @@
         res = testdir.runpytest(p.basename)
         assert res.ret == 0
 
+    def test_unknown_option(self, testdir):
+        result = testdir.runpytest("--qwlkej")
+        result.stderr.fnmatch_lines("""
+            *unrecognized*
+        """)
+
 
 class TestInvocationVariants:
     def test_earlyinit(self, testdir):


https://bitbucket.org/hpk42/pytest/commits/d9cd8600a979/
Changeset:   d9cd8600a979
User:        hpk42
Date:        2014-04-01 14:32:12
Summary:     remove non-documented per-conftest capturing option and simplify/refactor all code accordingly.  Also make capturing more robust against tests closing FD1/2 and against pdb.set_trace() calls.
Affected #:  4 files

diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -21,9 +21,10 @@
 def pytest_addoption(parser):
     group = parser.getgroup("general")
     group._addoption(
-        '--capture', action="store", default=None,
+        '--capture', action="store", 
+        default="fd" if hasattr(os, "dup") else "sys",
         metavar="method", choices=['fd', 'sys', 'no'],
-        help="per-test capturing method: one of fd (default)|sys|no.")
+        help="per-test capturing method: one of fd|sys|no.")
     group._addoption(
         '-s', action="store_const", const="no", dest="capture",
         help="shortcut for --capture=no.")
@@ -32,16 +33,13 @@
 @pytest.mark.tryfirst
 def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
     ns = parser.parse_known_args(args)
+    pluginmanager = early_config.pluginmanager
     method = ns.capture
-    if not method:
-        method = "fd"
-    if method == "fd" and not hasattr(os, "dup"):
-        method = "sys"
-    pluginmanager = early_config.pluginmanager
     if method != "no":
         dupped_stdout = safe_text_dupfile(sys.stdout, "wb")
         pluginmanager.register(dupped_stdout, "dupped_stdout")
             #pluginmanager.add_shutdown(dupped_stdout.close)
+
     capman = CaptureManager(method)
     pluginmanager.register(capman, "capturemanager")
 
@@ -55,7 +53,7 @@
     pluginmanager.add_shutdown(silence_logging_at_shutdown)
 
     # finally trigger conftest loading but while capturing (issue93)
-    capman.resumecapture()
+    capman.init_capturings()
     try:
         try:
             return __multicall__.execute()
@@ -67,11 +65,9 @@
         raise
 
 
-
 class CaptureManager:
-    def __init__(self, defaultmethod=None):
-        self._method2capture = {}
-        self._defaultmethod = defaultmethod
+    def __init__(self, method):
+        self._method = method
 
     def _getcapture(self, method):
         if method == "fd":
@@ -83,53 +79,27 @@
         else:
             raise ValueError("unknown capturing method: %r" % method)
 
-    def _getmethod(self, config, fspath):
-        if config.option.capture:
-            method = config.option.capture
-        else:
-            try:
-                method = config._conftest.rget("option_capture", path=fspath)
-            except KeyError:
-                method = "fd"
-        if method == "fd" and not hasattr(os, 'dup'):  # e.g. jython
-            method = "sys"
-        return method
+    def init_capturings(self):
+        assert not hasattr(self, "_capturing")
+        self._capturing = self._getcapture(self._method)
+        self._capturing.start_capturing()
 
     def reset_capturings(self):
-        for cap in self._method2capture.values():
+        cap = self.__dict__.pop("_capturing", None)
+        if cap is not None:
             cap.pop_outerr_to_orig()
             cap.stop_capturing()
-        self._method2capture.clear()
 
-    def resumecapture_item(self, item):
-        method = self._getmethod(item.config, item.fspath)
-        return self.resumecapture(method)
+    def resumecapture(self):
+        self._capturing.resume_capturing()
 
-    def resumecapture(self, method=None):
-        if hasattr(self, '_capturing'):
-            raise ValueError(
-                "cannot resume, already capturing with %r" %
-                (self._capturing,))
-        if method is None:
-            method = self._defaultmethod
-        cap = self._method2capture.get(method)
-        self._capturing = method
-        if cap is None:
-            self._method2capture[method] = cap = self._getcapture(method)
-            cap.start_capturing()
-        else:
-            cap.resume_capturing()
-
-    def suspendcapture(self, item=None):
+    def suspendcapture(self, in_=False):
         self.deactivate_funcargs()
-        method = self.__dict__.pop("_capturing", None)
-        outerr = "", ""
-        if method is not None:
-            cap = self._method2capture.get(method)
-            if cap is not None:
-                outerr = cap.readouterr()
-                cap.suspend_capturing()
-        return outerr
+        cap = getattr(self, "_capturing", None)
+        if cap is not None:
+            outerr = cap.readouterr()
+            cap.suspend_capturing(in_=in_)
+            return outerr
 
     def activate_funcargs(self, pyfuncitem):
         capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None)
@@ -142,28 +112,20 @@
         if capfuncarg is not None:
             capfuncarg.close()
 
-    @pytest.mark.hookwrapper
+    @pytest.mark.tryfirst
     def pytest_make_collect_report(self, __multicall__, collector):
-        method = self._getmethod(collector.config, collector.fspath)
+        if not isinstance(collector, pytest.File):
+            return
+        self.resumecapture()
         try:
-            self.resumecapture(method)
-        except ValueError:
-            yield
-            # recursive collect, XXX refactor capturing
-            # to allow for more lightweight recursive capturing
-            return
-        yield
-        out, err = self.suspendcapture()
-        # XXX getting the report from the ongoing hook call is a bit
-        # of a hack.  We need to think about capturing during collection
-        # and find out if it's really needed fine-grained (per
-        # collector).
-        if __multicall__.results:
-            rep = __multicall__.results[0]
-            if out:
-                rep.sections.append(("Captured stdout", out))
-            if err:
-                rep.sections.append(("Captured stderr", err))
+            rep = __multicall__.execute()
+        finally:
+            out, err = self.suspendcapture()
+        if out:
+            rep.sections.append(("Captured stdout", out))
+        if err:
+            rep.sections.append(("Captured stderr", err))
+        return rep
 
     @pytest.mark.hookwrapper
     def pytest_runtest_setup(self, item):
@@ -192,9 +154,9 @@
 
     @contextlib.contextmanager
     def item_capture_wrapper(self, item, when):
-        self.resumecapture_item(item)
+        self.resumecapture()
         yield
-        out, err = self.suspendcapture(item)
+        out, err = self.suspendcapture()
         item.add_report_section(when, "out", out)
         item.add_report_section(when, "err", err)
 
@@ -238,14 +200,14 @@
     def close(self):
         cap = self.__dict__.pop("_capture", None)
         if cap is not None:
-            cap.pop_outerr_to_orig()
+            self._outerr = cap.pop_outerr_to_orig()
             cap.stop_capturing()
 
     def readouterr(self):
         try:
             return self._capture.readouterr()
         except AttributeError:
-            return "", ""
+            return self._outerr
 
 
 def safe_text_dupfile(f, mode, default_encoding="UTF8"):
@@ -311,18 +273,25 @@
             self.out.writeorg(out)
         if err:
             self.err.writeorg(err)
+        return out, err
 
-    def suspend_capturing(self):
+    def suspend_capturing(self, in_=False):
         if self.out:
             self.out.suspend()
         if self.err:
             self.err.suspend()
+        if in_ and self.in_:
+            self.in_.suspend()
+            self._in_suspended = True
 
     def resume_capturing(self):
         if self.out:
             self.out.resume()
         if self.err:
             self.err.resume()
+        if hasattr(self, "_in_suspended"):
+            self.in_.resume()
+            del self._in_suspended
 
     def stop_capturing(self):
         """ stop capturing and reset capturing streams """
@@ -393,7 +362,8 @@
                 res = py.builtin._totext(res, enc, "replace")
             f.truncate(0)
             f.seek(0)
-        return res
+            return res
+        return ''
 
     def done(self):
         """ stop capturing, restore streams, return original capture file,

diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce _pytest/pdb.py
--- a/_pytest/pdb.py
+++ b/_pytest/pdb.py
@@ -34,7 +34,7 @@
         if self._pluginmanager is not None:
             capman = self._pluginmanager.getplugin("capturemanager")
             if capman:
-                capman.reset_capturings()
+                capman.suspendcapture(in_=True)
             tw = py.io.TerminalWriter()
             tw.line()
             tw.sep(">", "PDB set_trace (IO-capturing turned off)")
@@ -45,8 +45,8 @@
     def pytest_exception_interact(self, node, call, report):
         capman = node.config.pluginmanager.getplugin("capturemanager")
         if capman:
-            capman.reset_capturings()
-        return _enter_pdb(node, call.excinfo, report)
+            capman.suspendcapture(in_=True)
+        _enter_pdb(node, call.excinfo, report)
 
     def pytest_internalerror(self, excrepr, excinfo):
         for line in str(excrepr).split("\n"):

diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -53,79 +53,54 @@
 
 
 class TestCaptureManager:
-    def test_getmethod_default_no_fd(self, testdir, monkeypatch):
-        config = testdir.parseconfig(testdir.tmpdir)
-        assert config.getvalue("capture") is None
-        capman = CaptureManager()
+    def test_getmethod_default_no_fd(self, monkeypatch):
+        from _pytest.capture import pytest_addoption
+        from _pytest.config import Parser
+        parser = Parser()
+        pytest_addoption(parser)
+        default = parser._groups[0].options[0].default
+        assert default == "fd" if hasattr(os, "dup") else "sys"
+        parser = Parser()
         monkeypatch.delattr(os, 'dup', raising=False)
-        try:
-            assert capman._getmethod(config, None) == "sys"
-        finally:
-            monkeypatch.undo()
-
-    @pytest.mark.parametrize("mode", "no fd sys".split())
-    def test_configure_per_fspath(self, testdir, mode):
-        config = testdir.parseconfig(testdir.tmpdir)
-        capman = CaptureManager()
-        hasfd = hasattr(os, 'dup')
-        if hasfd:
-            assert capman._getmethod(config, None) == "fd"
-        else:
-            assert capman._getmethod(config, None) == "sys"
-
-        if not hasfd and mode == 'fd':
-            return
-        sub = testdir.tmpdir.mkdir("dir" + mode)
-        sub.ensure("__init__.py")
-        sub.join("conftest.py").write('option_capture = %r' % mode)
-        assert capman._getmethod(config, sub.join("test_hello.py")) == mode
+        pytest_addoption(parser)
+        assert parser._groups[0].options[0].default == "sys"
 
     @needsosdup
-    @pytest.mark.parametrize("method", ['no', 'fd', 'sys'])
+    @pytest.mark.parametrize("method", 
+        ['no', 'sys', pytest.mark.skipif('not hasattr(os, "dup")', 'fd')])
     def test_capturing_basic_api(self, method):
         capouter = StdCaptureFD()
         old = sys.stdout, sys.stderr, sys.stdin
         try:
-            capman = CaptureManager()
-            # call suspend without resume or start
-            outerr = capman.suspendcapture()
+            capman = CaptureManager(method)
+            capman.init_capturings()
             outerr = capman.suspendcapture()
             assert outerr == ("", "")
-            capman.resumecapture(method)
+            outerr = capman.suspendcapture()
+            assert outerr == ("", "")
             print ("hello")
             out, err = capman.suspendcapture()
             if method == "no":
                 assert old == (sys.stdout, sys.stderr, sys.stdin)
             else:
+                assert not out
+            capman.resumecapture()
+            print ("hello")
+            out, err = capman.suspendcapture()
+            if method != "no":
                 assert out == "hello\n"
-            capman.resumecapture(method)
-            out, err = capman.suspendcapture()
-            assert not out and not err
             capman.reset_capturings()
         finally:
             capouter.stop_capturing()
 
     @needsosdup
-    def test_juggle_capturings(self, testdir):
+    def test_init_capturing(self):
         capouter = StdCaptureFD()
         try:
-            #config = testdir.parseconfig(testdir.tmpdir)
-            capman = CaptureManager()
-            try:
-                capman.resumecapture("fd")
-                pytest.raises(ValueError, 'capman.resumecapture("fd")')
-                pytest.raises(ValueError, 'capman.resumecapture("sys")')
-                os.write(1, "hello\n".encode('ascii'))
-                out, err = capman.suspendcapture()
-                assert out == "hello\n"
-                capman.resumecapture("sys")
-                os.write(1, "hello\n".encode('ascii'))
-                py.builtin.print_("world", file=sys.stderr)
-                out, err = capman.suspendcapture()
-                assert not out
-                assert err == "world\n"
-            finally:
-                capman.reset_capturings()
+            capman = CaptureManager("fd")
+            capman.init_capturings()
+            pytest.raises(AssertionError, "capman.init_capturings()")
+            capman.reset_capturings()
         finally:
             capouter.stop_capturing()
 
@@ -991,7 +966,7 @@
         def test_close():
             os.close(1)
         def test_capture_again():
-            os.write(1, "hello\\n")
+            os.write(1, b"hello\\n")
             assert 0
     """)
     result = testdir.runpytest()

diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce testing/test_pdb.py
--- a/testing/test_pdb.py
+++ b/testing/test_pdb.py
@@ -167,6 +167,26 @@
         if child.isalive():
             child.wait()
 
+    def test_set_trace_capturing_afterwards(self, testdir):
+        p1 = testdir.makepyfile("""
+            import pdb
+            def test_1():
+                pdb.set_trace()
+            def test_2():
+                print ("hello")
+                assert 0
+        """)
+        child = testdir.spawn_pytest(str(p1))
+        child.expect("test_1")
+        child.send("c\n")
+        child.expect("test_2")
+        child.expect("Captured")
+        child.expect("hello")
+        child.sendeof()
+        child.read()
+        if child.isalive():
+            child.wait()
+
     @xfail_if_pdbpp_installed
     def test_pdb_interaction_doctest(self, testdir):
         p1 = testdir.makepyfile("""


https://bitbucket.org/hpk42/pytest/commits/d534ac2d68ec/
Changeset:   d534ac2d68ec
User:        hpk42
Date:        2014-04-01 15:03:17
Summary:     remove dupped_stdout logic and related changes, also simplify pytest_runtest_* calls to not use a contextlib with-decorator anymore.
Affected #:  7 files

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -26,6 +26,10 @@
 - fix issue412: messing with stdout/stderr FD-level streams is now
   captured without crashes.
 
+- simplified internal capturing mechanism and made it more robust
+  against tests or setups changing FD1/FD2, also better integrated
+  now with pytest.pdb() in single tests.
+
 
 2.5.2
 -----------------------------------

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/capture.py
--- a/_pytest/capture.py
+++ b/_pytest/capture.py
@@ -7,7 +7,6 @@
 import sys
 import os
 from tempfile import TemporaryFile
-import contextlib
 
 import py
 import pytest
@@ -34,13 +33,9 @@
 def pytest_load_initial_conftests(early_config, parser, args, __multicall__):
     ns = parser.parse_known_args(args)
     pluginmanager = early_config.pluginmanager
-    method = ns.capture
-    if method != "no":
-        dupped_stdout = safe_text_dupfile(sys.stdout, "wb")
-        pluginmanager.register(dupped_stdout, "dupped_stdout")
-            #pluginmanager.add_shutdown(dupped_stdout.close)
-
-    capman = CaptureManager(method)
+    if ns.capture == "no":
+        return
+    capman = CaptureManager(ns.capture)
     pluginmanager.register(capman, "capturemanager")
 
     # make sure that capturemanager is properly reset at final shutdown
@@ -129,20 +124,23 @@
 
     @pytest.mark.hookwrapper
     def pytest_runtest_setup(self, item):
-        with self.item_capture_wrapper(item, "setup"):
-            yield
+        self.resumecapture()
+        yield
+        self.suspendcapture_item(item, "setup")
 
     @pytest.mark.hookwrapper
     def pytest_runtest_call(self, item):
-        with self.item_capture_wrapper(item, "call"):
-            self.activate_funcargs(item)
-            yield
-            #self.deactivate_funcargs() called from ctx's suspendcapture()
+        self.resumecapture()
+        self.activate_funcargs(item)
+        yield
+        #self.deactivate_funcargs() called from suspendcapture()
+        self.suspendcapture_item(item, "call")
 
     @pytest.mark.hookwrapper
     def pytest_runtest_teardown(self, item):
-        with self.item_capture_wrapper(item, "teardown"):
-            yield
+        self.resumecapture()
+        yield
+        self.suspendcapture_item(item, "teardown")
 
     @pytest.mark.tryfirst
     def pytest_keyboard_interrupt(self, excinfo):
@@ -152,10 +150,7 @@
     def pytest_internalerror(self, excinfo):
         self.reset_capturings()
 
-    @contextlib.contextmanager
-    def item_capture_wrapper(self, item, when):
-        self.resumecapture()
-        yield
+    def suspendcapture_item(self, item, when):
         out, err = self.suspendcapture()
         item.add_report_section(when, "out", out)
         item.add_report_section(when, "err", err)

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/genscript.py
--- a/_pytest/genscript.py
+++ b/_pytest/genscript.py
@@ -60,7 +60,6 @@
 def pytest_cmdline_main(config):
     genscript = config.getvalue("genscript")
     if genscript:
-        #tw = config.get_terminal_writer()
         tw = py.io.TerminalWriter()
         deps =  ['py', '_pytest', 'pytest']
         if sys.version_info < (2,7):

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -47,8 +47,6 @@
 
 def pytest_cmdline_main(config):
     if config.option.version:
-        capman = config.pluginmanager.getplugin("capturemanager")
-        capman.reset_capturings()
         p = py.path.local(pytest.__file__)
         sys.stderr.write("This is pytest version %s, imported from %s\n" %
             (pytest.__version__, p))
@@ -64,7 +62,7 @@
         return 0
 
 def showhelp(config):
-    tw = config.get_terminal_writer()
+    tw = py.io.TerminalWriter()
     tw.write(config._parser.optparser.format_help())
     tw.line()
     tw.line()

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/mark.py
--- a/_pytest/mark.py
+++ b/_pytest/mark.py
@@ -40,7 +40,7 @@
 def pytest_cmdline_main(config):
     if config.option.markers:
         config.do_configure()
-        tw = config.get_terminal_writer()
+        tw = py.io.TerminalWriter()
         for line in config.getini("markers"):
             name, rest = line.split(":", 1)
             tw.write("@pytest.mark.%s:" % name, bold=True)

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -885,7 +885,7 @@
         nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:]))
         nodeid.replace(session.fspath.sep, "/")
 
-    tw = config.get_terminal_writer()
+    tw = py.io.TerminalWriter()
     verbose = config.getvalue("verbose")
 
     fm = session._fixturemanager

diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -36,10 +36,7 @@
 
 def pytest_configure(config):
     config.option.verbose -= config.option.quiet
-    out = config.pluginmanager.getplugin("dupped_stdout")
-    #if out is None:
-    #    out = sys.stdout
-    reporter = TerminalReporter(config, out)
+    reporter = TerminalReporter(config, sys.stdout)
     config.pluginmanager.register(reporter, 'terminalreporter')
     if config.option.debug or config.option.traceconfig:
         def mywriter(tags, args):
@@ -47,11 +44,6 @@
             reporter.write_line("[traceconfig] " + msg)
         config.trace.root.setprocessor("pytest:config", mywriter)
 
-def get_terminal_writer(config):
-    tr = config.pluginmanager.getplugin("terminalreporter")
-    return tr._tw
-
-
 def getreportopt(config):
     reportopts = ""
     optvalue = config.option.report
@@ -104,7 +96,6 @@
         self.startdir = self.curdir = py.path.local()
         if file is None:
             file = py.std.sys.stdout
-        #assert file.encoding
         self._tw = self.writer = py.io.TerminalWriter(file)
         if self.config.option.color == 'yes':
             self._tw.hasmarkup = True


https://bitbucket.org/hpk42/pytest/commits/f40e7ec514f3/
Changeset:   f40e7ec514f3
User:        hpk42
Date:        2014-04-01 15:06:44
Summary:     merge main
Affected #:  4 files

diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb AUTHORS
--- a/AUTHORS
+++ b/AUTHORS
@@ -40,3 +40,4 @@
 Jurko Gospodnetić
 Marc Schlaich
 Christopher Gilling
+Daniel Grana

diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -26,11 +26,12 @@
 - fix issue412: messing with stdout/stderr FD-level streams is now
   captured without crashes.
 
+- fix issue483: trial/py33 works now properly.  Thanks Daniel Grana for PR.
+
 - simplified internal capturing mechanism and made it more robust
   against tests or setups changing FD1/FD2, also better integrated
   now with pytest.pdb() in single tests.
 
-
 2.5.2
 -----------------------------------
 

diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -154,7 +154,7 @@
     if isinstance(item, TestCaseFunction):
         if 'twisted.trial.unittest' in sys.modules:
             ut = sys.modules['twisted.python.failure']
-            Failure__init__ = ut.Failure.__init__.im_func
+            Failure__init__ = ut.Failure.__init__
             check_testcase_implements_trial_reporter()
             def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
                 captureVars=None):

diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 distshare={homedir}/.tox/distshare
-envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,trial
+envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial
 
 [testenv]
 changedir=testing
@@ -45,7 +45,7 @@
 
 [testenv:py33-pexpect]
 changedir=testing
-basepython=python2.7
+basepython=python3.3
 deps={[testenv:py27-pexpect]deps}
 commands=
   py.test -rfsxX test_pdb.py test_terminal.py test_unittest.py
@@ -61,12 +61,22 @@
   py.test -n3 -rfsxX \
         --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing}
 
-[testenv:trial]
+[testenv:py27-trial]
 changedir=.
+basepython=python2.7
 deps=twisted
 commands=
   py.test -rsxf \
         --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py}
+
+[testenv:py33-trial]
+changedir=.
+basepython=python3.3
+deps={[testenv:py27-trial]deps}
+commands=
+  py.test -rsxf \
+        --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py}
+
 [testenv:doctest]
 changedir=.
 commands=py.test --doctest-modules _pytest

Repository URL: https://bitbucket.org/hpk42/pytest/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.


More information about the pytest-commit mailing list