merge
This commit is contained in:
		
						commit
						b5467645d3
					
				|  | @ -1,12 +1,13 @@ | ||||||
| """ | """ | ||||||
|     per-test stdout/stderr capturing mechanisms, | per-test stdout/stderr capturing mechanism. | ||||||
|     ``capsys`` and ``capfd`` function arguments. | 
 | ||||||
| """ | """ | ||||||
| # note: py.io capture was where copied from | from __future__ import with_statement | ||||||
| # pylib 1.4.20.dev2 (rev 13d9af95547e) | 
 | ||||||
| import sys | import sys | ||||||
| import os | import os | ||||||
| import tempfile | from tempfile import TemporaryFile | ||||||
|  | import contextlib | ||||||
| 
 | 
 | ||||||
| import py | import py | ||||||
| import pytest | import pytest | ||||||
|  | @ -58,8 +59,18 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__): | ||||||
|         method = "fd" |         method = "fd" | ||||||
|     if method == "fd" and not hasattr(os, "dup"): |     if method == "fd" and not hasattr(os, "dup"): | ||||||
|         method = "sys" |         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) |     capman = CaptureManager(method) | ||||||
|     early_config.pluginmanager.register(capman, "capturemanager") |     pluginmanager.register(capman, "capturemanager") | ||||||
| 
 | 
 | ||||||
|     # make sure that capturemanager is properly reset at final shutdown |     # make sure that capturemanager is properly reset at final shutdown | ||||||
|     def teardown(): |     def teardown(): | ||||||
|  | @ -68,13 +79,13 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__): | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|     early_config.pluginmanager.add_shutdown(teardown) |     pluginmanager.add_shutdown(teardown) | ||||||
| 
 | 
 | ||||||
|     # make sure logging does not raise exceptions at the end |     # make sure logging does not raise exceptions at the end | ||||||
|     def silence_logging_at_shutdown(): |     def silence_logging_at_shutdown(): | ||||||
|         if "logging" in sys.modules: |         if "logging" in sys.modules: | ||||||
|             sys.modules["logging"].raiseExceptions = False |             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) |     # finally trigger conftest loading but while capturing (issue93) | ||||||
|     capman.resumecapture() |     capman.resumecapture() | ||||||
|  | @ -89,53 +100,19 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__): | ||||||
|         raise |         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: | class CaptureManager: | ||||||
|     def __init__(self, defaultmethod=None): |     def __init__(self, defaultmethod=None): | ||||||
|         self._method2capture = {} |         self._method2capture = {} | ||||||
|         self._defaultmethod = defaultmethod |         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): |     def _getcapture(self, method): | ||||||
|         if method == "fd": |         if method == "fd": | ||||||
|             return StdCaptureFD( |             return StdCaptureBase(out=True, err=True, Capture=FDCapture) | ||||||
|                 out=self._maketempfile(), |  | ||||||
|                 err=self._maketempfile(), |  | ||||||
|             ) |  | ||||||
|         elif method == "sys": |         elif method == "sys": | ||||||
|             return StdCapture( |             return StdCaptureBase(out=True, err=True, Capture=SysCapture) | ||||||
|                 out=self._makestringio(), |  | ||||||
|                 err=self._makestringio(), |  | ||||||
|             ) |  | ||||||
|         elif method == "no": |         elif method == "no": | ||||||
|             return NoCapture() |             return StdCaptureBase(out=False, err=False, in_=False) | ||||||
|         else: |         else: | ||||||
|             raise ValueError("unknown capturing method: %r" % method) |             raise ValueError("unknown capturing method: %r" % method) | ||||||
| 
 | 
 | ||||||
|  | @ -153,12 +130,12 @@ class CaptureManager: | ||||||
| 
 | 
 | ||||||
|     def reset_capturings(self): |     def reset_capturings(self): | ||||||
|         for cap in self._method2capture.values(): |         for cap in self._method2capture.values(): | ||||||
|             cap.reset() |             cap.pop_outerr_to_orig() | ||||||
|  |             cap.stop_capturing() | ||||||
|  |         self._method2capture.clear() | ||||||
| 
 | 
 | ||||||
|     def resumecapture_item(self, item): |     def resumecapture_item(self, item): | ||||||
|         method = self._getmethod(item.config, item.fspath) |         method = self._getmethod(item.config, item.fspath) | ||||||
|         if not hasattr(item, 'outerr'): |  | ||||||
|             item.outerr = ('', '')  # we accumulate outerr on the item |  | ||||||
|         return self.resumecapture(method) |         return self.resumecapture(method) | ||||||
| 
 | 
 | ||||||
|     def resumecapture(self, method=None): |     def resumecapture(self, method=None): | ||||||
|  | @ -172,87 +149,85 @@ class CaptureManager: | ||||||
|         self._capturing = method |         self._capturing = method | ||||||
|         if cap is None: |         if cap is None: | ||||||
|             self._method2capture[method] = cap = self._getcapture(method) |             self._method2capture[method] = cap = self._getcapture(method) | ||||||
|             cap.startall() |             cap.start_capturing() | ||||||
|         else: |         else: | ||||||
|             cap.resume() |             cap.pop_outerr_to_orig() | ||||||
| 
 | 
 | ||||||
|     def suspendcapture(self, item=None): |     def suspendcapture(self, item=None): | ||||||
|         self.deactivate_funcargs() |         self.deactivate_funcargs() | ||||||
|         if hasattr(self, '_capturing'): |         method = self.__dict__.pop("_capturing", None) | ||||||
|             method = self._capturing |         if method is not None: | ||||||
|             cap = self._method2capture.get(method) |             cap = self._method2capture.get(method) | ||||||
|             if cap is not None: |             if cap is not None: | ||||||
|                 outerr = cap.suspend() |                 return cap.readouterr() | ||||||
|             del self._capturing |  | ||||||
|             if item: |  | ||||||
|                 outerr = (item.outerr[0] + outerr[0], |  | ||||||
|                           item.outerr[1] + outerr[1]) |  | ||||||
|             return outerr |  | ||||||
|         if hasattr(item, 'outerr'): |  | ||||||
|             return item.outerr |  | ||||||
|         return "", "" |         return "", "" | ||||||
| 
 | 
 | ||||||
|     def activate_funcargs(self, pyfuncitem): |     def activate_funcargs(self, pyfuncitem): | ||||||
|         funcargs = getattr(pyfuncitem, "funcargs", None) |         capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) | ||||||
|         if funcargs is not None: |         if capfuncarg is not None: | ||||||
|             for name, capfuncarg in funcargs.items(): |             capfuncarg._start() | ||||||
|                 if name in ('capsys', 'capfd'): |             self._capfuncarg = capfuncarg | ||||||
|                     assert not hasattr(self, '_capturing_funcarg') |  | ||||||
|                     self._capturing_funcarg = capfuncarg |  | ||||||
|                     capfuncarg._start() |  | ||||||
| 
 | 
 | ||||||
|     def deactivate_funcargs(self): |     def deactivate_funcargs(self): | ||||||
|         capturing_funcarg = getattr(self, '_capturing_funcarg', None) |         capfuncarg = self.__dict__.pop("_capfuncarg", None) | ||||||
|         if capturing_funcarg: |         if capfuncarg is not None: | ||||||
|             outerr = capturing_funcarg._finalize() |             capfuncarg.close() | ||||||
|             del self._capturing_funcarg |  | ||||||
|             return outerr |  | ||||||
| 
 | 
 | ||||||
|  |     @pytest.mark.hookwrapper | ||||||
|     def pytest_make_collect_report(self, __multicall__, collector): |     def pytest_make_collect_report(self, __multicall__, collector): | ||||||
|         method = self._getmethod(collector.config, collector.fspath) |         method = self._getmethod(collector.config, collector.fspath) | ||||||
|         try: |         try: | ||||||
|             self.resumecapture(method) |             self.resumecapture(method) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|  |             yield | ||||||
|             # recursive collect, XXX refactor capturing |             # recursive collect, XXX refactor capturing | ||||||
|             # to allow for more lightweight recursive capturing |             # to allow for more lightweight recursive capturing | ||||||
|             return |             return | ||||||
|         try: |         yield | ||||||
|             rep = __multicall__.execute() |         out, err = self.suspendcapture() | ||||||
|         finally: |         # XXX getting the report from the ongoing hook call is a bit | ||||||
|             outerr = self.suspendcapture() |         # of a hack.  We need to think about capturing during collection | ||||||
|         addouterr(rep, outerr) |         # and find out if it's really needed fine-grained (per | ||||||
|         return rep |         # 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.tryfirst |     @pytest.mark.hookwrapper | ||||||
|     def pytest_runtest_setup(self, item): |     def pytest_runtest_setup(self, item): | ||||||
|         self.resumecapture_item(item) |         with self.item_capture_wrapper(item, "setup"): | ||||||
|  |             yield | ||||||
| 
 | 
 | ||||||
|     @pytest.mark.tryfirst |     @pytest.mark.hookwrapper | ||||||
|     def pytest_runtest_call(self, item): |     def pytest_runtest_call(self, item): | ||||||
|         self.resumecapture_item(item) |         with self.item_capture_wrapper(item, "call"): | ||||||
|         self.activate_funcargs(item) |             self.activate_funcargs(item) | ||||||
|  |             yield | ||||||
|  |             #self.deactivate_funcargs() called from ctx's suspendcapture() | ||||||
| 
 | 
 | ||||||
|     @pytest.mark.tryfirst |     @pytest.mark.hookwrapper | ||||||
|     def pytest_runtest_teardown(self, item): |     def pytest_runtest_teardown(self, item): | ||||||
|         self.resumecapture_item(item) |         with self.item_capture_wrapper(item, "teardown"): | ||||||
| 
 |             yield | ||||||
|     def pytest_keyboard_interrupt(self, excinfo): |  | ||||||
|         if hasattr(self, '_capturing'): |  | ||||||
|             self.suspendcapture() |  | ||||||
| 
 | 
 | ||||||
|     @pytest.mark.tryfirst |     @pytest.mark.tryfirst | ||||||
|     def pytest_runtest_makereport(self, __multicall__, item, call): |     def pytest_keyboard_interrupt(self, excinfo): | ||||||
|         funcarg_outerr = self.deactivate_funcargs() |         self.reset_capturings() | ||||||
|         rep = __multicall__.execute() | 
 | ||||||
|         outerr = self.suspendcapture(item) |     @pytest.mark.tryfirst | ||||||
|         if funcarg_outerr is not None: |     def pytest_internalerror(self, excinfo): | ||||||
|             outerr = (outerr[0] + funcarg_outerr[0], |         self.reset_capturings() | ||||||
|                       outerr[1] + funcarg_outerr[1]) | 
 | ||||||
|         addouterr(rep, outerr) |     @contextlib.contextmanager | ||||||
|         if not rep.passed or rep.when == "teardown": |     def item_capture_wrapper(self, item, when): | ||||||
|             outerr = ('', '') |         self.resumecapture_item(item) | ||||||
|         item.outerr = outerr |         yield | ||||||
|         return rep |         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" | error_capsysfderror = "cannot use capsys and capfd at the same time" | ||||||
| 
 | 
 | ||||||
|  | @ -264,8 +239,8 @@ def pytest_funcarg__capsys(request): | ||||||
|     """ |     """ | ||||||
|     if "capfd" in request._funcargs: |     if "capfd" in request._funcargs: | ||||||
|         raise request.raiseerror(error_capsysfderror) |         raise request.raiseerror(error_capsysfderror) | ||||||
|     return CaptureFixture(StdCapture) |     request.node._capfuncarg = c = CaptureFixture(SysCapture) | ||||||
| 
 |     return c | ||||||
| 
 | 
 | ||||||
| def pytest_funcarg__capfd(request): | def pytest_funcarg__capfd(request): | ||||||
|     """enables capturing of writes to file descriptors 1 and 2 and makes |     """enables capturing of writes to file descriptors 1 and 2 and makes | ||||||
|  | @ -276,89 +251,30 @@ def pytest_funcarg__capfd(request): | ||||||
|         request.raiseerror(error_capsysfderror) |         request.raiseerror(error_capsysfderror) | ||||||
|     if not hasattr(os, 'dup'): |     if not hasattr(os, 'dup'): | ||||||
|         pytest.skip("capfd funcarg needs os.dup") |         pytest.skip("capfd funcarg needs os.dup") | ||||||
|     return CaptureFixture(StdCaptureFD) |     request.node._capfuncarg = c = CaptureFixture(FDCapture) | ||||||
|  |     return c | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CaptureFixture: | class CaptureFixture: | ||||||
|     def __init__(self, captureclass): |     def __init__(self, captureclass): | ||||||
|         self._capture = captureclass() |         self.captureclass = captureclass | ||||||
| 
 | 
 | ||||||
|     def _start(self): |     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): |     def close(self): | ||||||
|         if hasattr(self, '_capture'): |         cap = self.__dict__.pop("_capture", None) | ||||||
|             outerr = self._outerr = self._capture.reset() |         if cap is not None: | ||||||
|             del self._capture |             cap.pop_outerr_to_orig() | ||||||
|             return outerr |             cap.stop_capturing() | ||||||
| 
 | 
 | ||||||
|     def readouterr(self): |     def readouterr(self): | ||||||
|         try: |         try: | ||||||
|             return self._capture.readouterr() |             return self._capture.readouterr() | ||||||
|         except AttributeError: |         except AttributeError: | ||||||
|             return self._outerr |             return "", "" | ||||||
| 
 |  | ||||||
|     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() |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): | def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): | ||||||
|  | @ -408,185 +324,148 @@ class EncodedFile(object): | ||||||
|         return getattr(self._stream, name) |         return getattr(self._stream, name) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Capture(object): | class StdCaptureBase(object): | ||||||
|     def reset(self): |     out = err = in_ = None | ||||||
|         """ 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 |  | ||||||
| 
 | 
 | ||||||
|     def suspend(self): |     def __init__(self, out=True, err=True, in_=True, Capture=None): | ||||||
|         """ return current snapshot captures, memorize tempfiles. """ |  | ||||||
|         outerr = self.readouterr() |  | ||||||
|         outfile, errfile = self.done() |  | ||||||
|         return outerr |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 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_: |         if in_: | ||||||
|             try: |             self.in_ = Capture(0) | ||||||
|                 self.in_ = FDCapture( |  | ||||||
|                     0, tmpfile=None, |  | ||||||
|                     patchsys=patchsys) |  | ||||||
|             except OSError: |  | ||||||
|                 pass |  | ||||||
|         if out: |         if out: | ||||||
|             tmpfile = None |             self.out = Capture(1) | ||||||
|             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 err: | ||||||
|             if hasattr(err, 'write'): |             self.err = Capture(2) | ||||||
|                 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): |     def start_capturing(self): | ||||||
|         if hasattr(self, 'in_'): |         if self.in_: | ||||||
|             self.in_.start() |             self.in_.start() | ||||||
|         if hasattr(self, 'out'): |         if self.out: | ||||||
|             self.out.start() |             self.out.start() | ||||||
|         if hasattr(self, 'err'): |         if self.err: | ||||||
|             self.err.start() |             self.err.start() | ||||||
| 
 | 
 | ||||||
|     def resume(self): |     def pop_outerr_to_orig(self): | ||||||
|         """ resume capturing with original temp files. """ |         """ pop current snapshot out/err capture and flush to orig streams. """ | ||||||
|         self.startall() |         out, err = self.readouterr() | ||||||
|  |         if out: | ||||||
|  |             self.out.writeorg(out) | ||||||
|  |         if err: | ||||||
|  |             self.err.writeorg(err) | ||||||
| 
 | 
 | ||||||
|     def done(self, save=True): |     def stop_capturing(self): | ||||||
|         """ return (outfile, errfile) and stop capturing. """ |         """ stop capturing and reset capturing streams """ | ||||||
|         outfile = errfile = None |         if hasattr(self, '_reset'): | ||||||
|         if hasattr(self, 'out') and not self.out.tmpfile.closed: |             raise ValueError("was already stopped") | ||||||
|             outfile = self.out.done() |         self._reset = True | ||||||
|         if hasattr(self, 'err') and not self.err.tmpfile.closed: |         if self.out: | ||||||
|             errfile = self.err.done() |             self.out.done() | ||||||
|         if hasattr(self, 'in_'): |         if self.err: | ||||||
|  |             self.err.done() | ||||||
|  |         if self.in_: | ||||||
|             self.in_.done() |             self.in_.done() | ||||||
|         if save: |  | ||||||
|             self._save() |  | ||||||
|         return outfile, errfile |  | ||||||
| 
 | 
 | ||||||
|     def readouterr(self): |     def readouterr(self): | ||||||
|         """ return snapshot value of stdout/stderr capturings. """ |         """ return snapshot unicode value of stdout/stderr capturings. """ | ||||||
|         out = self._readsnapshot('out') |         return self._readsnapshot('out'), self._readsnapshot('err') | ||||||
|         err = self._readsnapshot('err') |  | ||||||
|         return out, err |  | ||||||
| 
 | 
 | ||||||
|     def _readsnapshot(self, name): |     def _readsnapshot(self, name): | ||||||
|         if hasattr(self, name): |         cap = getattr(self, name, None) | ||||||
|             f = getattr(self, name).tmpfile |         if cap is None: | ||||||
|         else: |             return "" | ||||||
|             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: | ||||||
|  |             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) |         f.seek(0) | ||||||
|         res = f.read() |         res = f.read() | ||||||
|         enc = getattr(f, "encoding", None) |         if res: | ||||||
|         if enc: |             enc = getattr(f, "encoding", None) | ||||||
|             res = py.builtin._totext(res, enc, "replace") |             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.truncate(0) | ||||||
|         f.seek(0) |         f.seek(0) | ||||||
|         return res |         return res | ||||||
| 
 | 
 | ||||||
|  |     def done(self): | ||||||
|  |         setattr(sys, self.name, self._old) | ||||||
|  |         self.tmpfile.close() | ||||||
| 
 | 
 | ||||||
| class StdCapture(Capture): |     def writeorg(self, data): | ||||||
|     """ This class allows to capture writes to sys.stdout|stderr "in-memory" |         self._old.write(data) | ||||||
|         and will raise errors on tries to read from sys.stdin. It only |         self._old.flush() | ||||||
|         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 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DontReadFromInput: | class DontReadFromInput: | ||||||
|  |  | ||||||
|  | @ -56,11 +56,15 @@ def _prepareconfig(args=None, plugins=None): | ||||||
|             raise ValueError("not a string or argument list: %r" % (args,)) |             raise ValueError("not a string or argument list: %r" % (args,)) | ||||||
|         args = py.std.shlex.split(args) |         args = py.std.shlex.split(args) | ||||||
|     pluginmanager = get_plugin_manager() |     pluginmanager = get_plugin_manager() | ||||||
|     if plugins: |     try: | ||||||
|         for plugin in plugins: |         if plugins: | ||||||
|             pluginmanager.register(plugin) |             for plugin in plugins: | ||||||
|     return pluginmanager.hook.pytest_cmdline_parse( |                 pluginmanager.register(plugin) | ||||||
|             pluginmanager=pluginmanager, args=args) |         return pluginmanager.hook.pytest_cmdline_parse( | ||||||
|  |                 pluginmanager=pluginmanager, args=args) | ||||||
|  |     except Exception: | ||||||
|  |         pluginmanager.ensure_shutdown() | ||||||
|  |         raise | ||||||
| 
 | 
 | ||||||
| class PytestPluginManager(PluginManager): | class PytestPluginManager(PluginManager): | ||||||
|     def __init__(self, hookspecs=[hookspec]): |     def __init__(self, hookspecs=[hookspec]): | ||||||
|  | @ -612,6 +616,9 @@ class Config(object): | ||||||
|         self.hook.pytest_logwarning(code=code, message=message, |         self.hook.pytest_logwarning(code=code, message=message, | ||||||
|                                     fslocation=None, nodeid=None) |                                     fslocation=None, nodeid=None) | ||||||
| 
 | 
 | ||||||
|  |     def get_terminal_writer(self): | ||||||
|  |         return self.pluginmanager.getplugin("terminalreporter")._tw | ||||||
|  | 
 | ||||||
|     def pytest_cmdline_parse(self, pluginmanager, args): |     def pytest_cmdline_parse(self, pluginmanager, args): | ||||||
|         assert self == pluginmanager.config, (self, pluginmanager.config) |         assert self == pluginmanager.config, (self, pluginmanager.config) | ||||||
|         self.parse(args) |         self.parse(args) | ||||||
|  |  | ||||||
|  | @ -240,18 +240,22 @@ class PluginManager(object): | ||||||
|             pass |             pass | ||||||
|         l = [] |         l = [] | ||||||
|         last = [] |         last = [] | ||||||
|  |         wrappers = [] | ||||||
|         for plugin in plugins: |         for plugin in plugins: | ||||||
|             try: |             try: | ||||||
|                 meth = getattr(plugin, attrname) |                 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: |             except AttributeError: | ||||||
|                 continue |                 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(last) | ||||||
|  |         l.extend(wrappers) | ||||||
|         self._listattrcache[key] = list(l) |         self._listattrcache[key] = list(l) | ||||||
|         return l |         return l | ||||||
| 
 | 
 | ||||||
|  | @ -272,6 +276,14 @@ def importplugin(importspec): | ||||||
| 
 | 
 | ||||||
| class MultiCall: | class MultiCall: | ||||||
|     """ execute a call into multiple python functions/methods. """ |     """ 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): |     def __init__(self, methods, kwargs, firstresult=False): | ||||||
|         self.methods = list(methods) |         self.methods = list(methods) | ||||||
|         self.kwargs = kwargs |         self.kwargs = kwargs | ||||||
|  | @ -283,16 +295,39 @@ class MultiCall: | ||||||
|         return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs) |         return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs) | ||||||
| 
 | 
 | ||||||
|     def execute(self): |     def execute(self): | ||||||
|         while self.methods: |         next_finalizers = [] | ||||||
|             method = self.methods.pop() |         try: | ||||||
|             kwargs = self.getkwargs(method) |             while self.methods: | ||||||
|             res = method(**kwargs) |                 method = self.methods.pop() | ||||||
|             if res is not None: |                 kwargs = self.getkwargs(method) | ||||||
|                 self.results.append(res) |                 if hasattr(method, "hookwrapper"): | ||||||
|                 if self.firstresult: |                     it = method(**kwargs) | ||||||
|                     return res |                     next = getattr(it, "next", None) | ||||||
|         if not self.firstresult: |                     if next is None: | ||||||
|             return self.results |                         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): |     def getkwargs(self, method): | ||||||
|         kwargs = {} |         kwargs = {} | ||||||
|  |  | ||||||
|  | @ -60,6 +60,7 @@ def pytest_addoption(parser): | ||||||
| def pytest_cmdline_main(config): | def pytest_cmdline_main(config): | ||||||
|     genscript = config.getvalue("genscript") |     genscript = config.getvalue("genscript") | ||||||
|     if genscript: |     if genscript: | ||||||
|  |         #tw = config.get_terminal_writer() | ||||||
|         tw = py.io.TerminalWriter() |         tw = py.io.TerminalWriter() | ||||||
|         deps =  ['py', '_pytest', 'pytest'] |         deps =  ['py', '_pytest', 'pytest'] | ||||||
|         if sys.version_info < (2,7): |         if sys.version_info < (2,7): | ||||||
|  |  | ||||||
|  | @ -47,6 +47,8 @@ def pytest_unconfigure(config): | ||||||
| 
 | 
 | ||||||
| def pytest_cmdline_main(config): | def pytest_cmdline_main(config): | ||||||
|     if config.option.version: |     if config.option.version: | ||||||
|  |         capman = config.pluginmanager.getplugin("capturemanager") | ||||||
|  |         capman.reset_capturings() | ||||||
|         p = py.path.local(pytest.__file__) |         p = py.path.local(pytest.__file__) | ||||||
|         sys.stderr.write("This is pytest version %s, imported from %s\n" % |         sys.stderr.write("This is pytest version %s, imported from %s\n" % | ||||||
|             (pytest.__version__, p)) |             (pytest.__version__, p)) | ||||||
|  | @ -62,7 +64,7 @@ def pytest_cmdline_main(config): | ||||||
|         return 0 |         return 0 | ||||||
| 
 | 
 | ||||||
| def showhelp(config): | def showhelp(config): | ||||||
|     tw = py.io.TerminalWriter() |     tw = config.get_terminal_writer() | ||||||
|     tw.write(config._parser.optparser.format_help()) |     tw.write(config._parser.optparser.format_help()) | ||||||
|     tw.line() |     tw.line() | ||||||
|     tw.line() |     tw.line() | ||||||
|  |  | ||||||
|  | @ -108,12 +108,14 @@ class LogXML(object): | ||||||
|         )) |         )) | ||||||
| 
 | 
 | ||||||
|     def _write_captured_output(self, report): |     def _write_captured_output(self, report): | ||||||
|         sec = dict(report.sections) |         for capname in ('out', 'err'): | ||||||
|         for name in ('out', 'err'): |             allcontent = "" | ||||||
|             content = sec.get("Captured std%s" % name) |             for name, content in report.get_sections("Captured std%s" % | ||||||
|             if content: |                                                     capname): | ||||||
|                 tag = getattr(Junit, 'system-'+name) |                 allcontent += content | ||||||
|                 self.append(tag(bin_xml_escape(content))) |             if allcontent: | ||||||
|  |                 tag = getattr(Junit, 'system-'+capname) | ||||||
|  |                 self.append(tag(bin_xml_escape(allcontent))) | ||||||
| 
 | 
 | ||||||
|     def append(self, obj): |     def append(self, obj): | ||||||
|         self.tests[-1].append(obj) |         self.tests[-1].append(obj) | ||||||
|  |  | ||||||
|  | @ -233,6 +233,7 @@ class Node(object): | ||||||
| 
 | 
 | ||||||
|         # used for storing artificial fixturedefs for direct parametrization |         # used for storing artificial fixturedefs for direct parametrization | ||||||
|         self._name2pseudofixturedef = {} |         self._name2pseudofixturedef = {} | ||||||
|  | 
 | ||||||
|         #self.extrainit() |         #self.extrainit() | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|  | @ -465,6 +466,14 @@ class Item(Node): | ||||||
|     """ |     """ | ||||||
|     nextitem = None |     nextitem = None | ||||||
| 
 | 
 | ||||||
|  |     def __init__(self, name, parent=None, config=None, session=None): | ||||||
|  |         super(Item, self).__init__(name, parent, config, session) | ||||||
|  |         self._report_sections = [] | ||||||
|  | 
 | ||||||
|  |     def add_report_section(self, when, key, content): | ||||||
|  |         if content: | ||||||
|  |             self._report_sections.append((when, key, content)) | ||||||
|  | 
 | ||||||
|     def reportinfo(self): |     def reportinfo(self): | ||||||
|         return self.fspath, None, "" |         return self.fspath, None, "" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ def pytest_addoption(parser): | ||||||
| def pytest_cmdline_main(config): | def pytest_cmdline_main(config): | ||||||
|     if config.option.markers: |     if config.option.markers: | ||||||
|         config.do_configure() |         config.do_configure() | ||||||
|         tw = py.io.TerminalWriter() |         tw = config.get_terminal_writer() | ||||||
|         for line in config.getini("markers"): |         for line in config.getini("markers"): | ||||||
|             name, rest = line.split(":", 1) |             name, rest = line.split(":", 1) | ||||||
|             tw.write("@pytest.mark.%s:" % name, bold=True) |             tw.write("@pytest.mark.%s:" % name, bold=True) | ||||||
|  |  | ||||||
|  | @ -16,49 +16,36 @@ def pytest_configure(config): | ||||||
|     if config.getvalue("usepdb"): |     if config.getvalue("usepdb"): | ||||||
|         config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') |         config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') | ||||||
| 
 | 
 | ||||||
|     old_trace = py.std.pdb.set_trace |     old = (py.std.pdb.set_trace, pytestPDB._pluginmanager) | ||||||
|     def fin(): |     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 |     py.std.pdb.set_trace = pytest.set_trace | ||||||
|  |     pytestPDB._pluginmanager = config.pluginmanager | ||||||
|     config._cleanup.append(fin) |     config._cleanup.append(fin) | ||||||
| 
 | 
 | ||||||
| class pytestPDB: | class pytestPDB: | ||||||
|     """ Pseudo PDB that defers to the real pdb. """ |     """ Pseudo PDB that defers to the real pdb. """ | ||||||
|     item = None |     _pluginmanager = None | ||||||
|     collector = None |  | ||||||
| 
 | 
 | ||||||
|     def set_trace(self): |     def set_trace(self): | ||||||
|         """ invoke PDB set_trace debugging, dropping any IO capturing. """ |         """ invoke PDB set_trace debugging, dropping any IO capturing. """ | ||||||
|         frame = sys._getframe().f_back |         frame = sys._getframe().f_back | ||||||
|         item = self.item or self.collector |         capman = None | ||||||
| 
 |         if self._pluginmanager is not None: | ||||||
|         if item is not None: |             capman = self._pluginmanager.getplugin("capturemanager") | ||||||
|             capman = item.config.pluginmanager.getplugin("capturemanager") |             if capman: | ||||||
|             out, err = capman.suspendcapture() |                 capman.reset_capturings() | ||||||
|             if hasattr(item, 'outerr'): |  | ||||||
|                 item.outerr = (item.outerr[0] + out, item.outerr[1] + err) |  | ||||||
|             tw = py.io.TerminalWriter() |             tw = py.io.TerminalWriter() | ||||||
|             tw.line() |             tw.line() | ||||||
|             tw.sep(">", "PDB set_trace (IO-capturing turned off)") |             tw.sep(">", "PDB set_trace (IO-capturing turned off)") | ||||||
|         py.std.pdb.Pdb().set_trace(frame) |         py.std.pdb.Pdb().set_trace(frame) | ||||||
| 
 | 
 | ||||||
| def pdbitem(item): |  | ||||||
|     pytestPDB.item = item |  | ||||||
| pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem |  | ||||||
| 
 |  | ||||||
| @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: | class PdbInvoke: | ||||||
|     def pytest_exception_interact(self, node, call, report): |     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) |         return _enter_pdb(node, call.excinfo, report) | ||||||
| 
 | 
 | ||||||
|     def pytest_internalerror(self, excrepr, excinfo): |     def pytest_internalerror(self, excrepr, excinfo): | ||||||
|  |  | ||||||
|  | @ -885,7 +885,7 @@ def _showfixtures_main(config, session): | ||||||
|         nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) |         nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) | ||||||
|         nodeid.replace(session.fspath.sep, "/") |         nodeid.replace(session.fspath.sep, "/") | ||||||
| 
 | 
 | ||||||
|     tw = py.io.TerminalWriter() |     tw = config.get_terminal_writer() | ||||||
|     verbose = config.getvalue("verbose") |     verbose = config.getvalue("verbose") | ||||||
| 
 | 
 | ||||||
|     fm = session._fixturemanager |     fm = session._fixturemanager | ||||||
|  |  | ||||||
|  | @ -135,14 +135,13 @@ class CallInfo: | ||||||
|         self.when = when |         self.when = when | ||||||
|         self.start = time() |         self.start = time() | ||||||
|         try: |         try: | ||||||
|             try: |             self.result = func() | ||||||
|                 self.result = func() |         except KeyboardInterrupt: | ||||||
|             except KeyboardInterrupt: |  | ||||||
|                 raise |  | ||||||
|             except: |  | ||||||
|                 self.excinfo = py.code.ExceptionInfo() |  | ||||||
|         finally: |  | ||||||
|             self.stop = time() |             self.stop = time() | ||||||
|  |             raise | ||||||
|  |         except: | ||||||
|  |             self.excinfo = py.code.ExceptionInfo() | ||||||
|  |         self.stop = time() | ||||||
| 
 | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         if self.excinfo: |         if self.excinfo: | ||||||
|  | @ -178,6 +177,11 @@ class BaseReport(object): | ||||||
|             except UnicodeEncodeError: |             except UnicodeEncodeError: | ||||||
|                 out.line("<unprintable longrepr>") |                 out.line("<unprintable longrepr>") | ||||||
| 
 | 
 | ||||||
|  |     def get_sections(self, prefix): | ||||||
|  |         for name, content in self.sections: | ||||||
|  |             if name.startswith(prefix): | ||||||
|  |                 yield prefix, content | ||||||
|  | 
 | ||||||
|     passed = property(lambda x: x.outcome == "passed") |     passed = property(lambda x: x.outcome == "passed") | ||||||
|     failed = property(lambda x: x.outcome == "failed") |     failed = property(lambda x: x.outcome == "failed") | ||||||
|     skipped = property(lambda x: x.outcome == "skipped") |     skipped = property(lambda x: x.outcome == "skipped") | ||||||
|  | @ -191,6 +195,7 @@ def pytest_runtest_makereport(item, call): | ||||||
|     duration = call.stop-call.start |     duration = call.stop-call.start | ||||||
|     keywords = dict([(x,1) for x in item.keywords]) |     keywords = dict([(x,1) for x in item.keywords]) | ||||||
|     excinfo = call.excinfo |     excinfo = call.excinfo | ||||||
|  |     sections = [] | ||||||
|     if not call.excinfo: |     if not call.excinfo: | ||||||
|         outcome = "passed" |         outcome = "passed" | ||||||
|         longrepr = None |         longrepr = None | ||||||
|  | @ -209,16 +214,18 @@ def pytest_runtest_makereport(item, call): | ||||||
|             else: # exception in setup or teardown |             else: # exception in setup or teardown | ||||||
|                 longrepr = item._repr_failure_py(excinfo, |                 longrepr = item._repr_failure_py(excinfo, | ||||||
|                                             style=item.config.option.tbstyle) |                                             style=item.config.option.tbstyle) | ||||||
|  |     for rwhen, key, content in item._report_sections: | ||||||
|  |         sections.append(("Captured std%s %s" %(key, rwhen), content)) | ||||||
|     return TestReport(item.nodeid, item.location, |     return TestReport(item.nodeid, item.location, | ||||||
|                       keywords, outcome, longrepr, when, |                       keywords, outcome, longrepr, when, | ||||||
|                       duration=duration) |                       sections, duration) | ||||||
| 
 | 
 | ||||||
| class TestReport(BaseReport): | class TestReport(BaseReport): | ||||||
|     """ Basic test report object (also used for setup and teardown calls if |     """ Basic test report object (also used for setup and teardown calls if | ||||||
|     they fail). |     they fail). | ||||||
|     """ |     """ | ||||||
|     def __init__(self, nodeid, location, |     def __init__(self, nodeid, location, keywords, outcome, | ||||||
|             keywords, outcome, longrepr, when, sections=(), duration=0, **extra): |                  longrepr, when, sections=(), duration=0, **extra): | ||||||
|         #: normalized collection node id |         #: normalized collection node id | ||||||
|         self.nodeid = nodeid |         self.nodeid = nodeid | ||||||
| 
 | 
 | ||||||
|  | @ -286,7 +293,8 @@ def pytest_make_collect_report(collector): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CollectReport(BaseReport): | 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.nodeid = nodeid | ||||||
|         self.outcome = outcome |         self.outcome = outcome | ||||||
|         self.longrepr = longrepr |         self.longrepr = longrepr | ||||||
|  |  | ||||||
|  | @ -36,7 +36,10 @@ def pytest_addoption(parser): | ||||||
| 
 | 
 | ||||||
| def pytest_configure(config): | def pytest_configure(config): | ||||||
|     config.option.verbose -= config.option.quiet |     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') |     config.pluginmanager.register(reporter, 'terminalreporter') | ||||||
|     if config.option.debug or config.option.traceconfig: |     if config.option.debug or config.option.traceconfig: | ||||||
|         def mywriter(tags, args): |         def mywriter(tags, args): | ||||||
|  | @ -44,6 +47,11 @@ def pytest_configure(config): | ||||||
|             reporter.write_line("[traceconfig] " + msg) |             reporter.write_line("[traceconfig] " + msg) | ||||||
|         config.trace.root.setprocessor("pytest:config", mywriter) |         config.trace.root.setprocessor("pytest:config", mywriter) | ||||||
| 
 | 
 | ||||||
|  | def get_terminal_writer(config): | ||||||
|  |     tr = config.pluginmanager.getplugin("terminalreporter") | ||||||
|  |     return tr._tw | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def getreportopt(config): | def getreportopt(config): | ||||||
|     reportopts = "" |     reportopts = "" | ||||||
|     optvalue = config.option.report |     optvalue = config.option.report | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ if __name__ == '__main__': | ||||||
|     import cProfile |     import cProfile | ||||||
|     import pytest |     import pytest | ||||||
|     import pstats |     import pstats | ||||||
|     script = sys.argv[1] if len(sys.argv) > 1 else "empty.py" |     script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py" | ||||||
|     stats = cProfile.run('pytest.cmdline.main([%r])' % script, 'prof') |     stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof') | ||||||
|     p = pstats.Stats("prof") |     p = pstats.Stats("prof") | ||||||
|     p.strip_dirs() |     p.strip_dirs() | ||||||
|     p.sort_stats('cumulative') |     p.sort_stats('cumulative') | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ from __future__ import with_statement | ||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| import py | import py | ||||||
|  | import tempfile | ||||||
| import pytest | import pytest | ||||||
| import contextlib | import contextlib | ||||||
| 
 | 
 | ||||||
|  | @ -44,6 +45,13 @@ def oswritebytes(fd, obj): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 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: | class TestCaptureManager: | ||||||
|     def test_getmethod_default_no_fd(self, testdir, monkeypatch): |     def test_getmethod_default_no_fd(self, testdir, monkeypatch): | ||||||
|         config = testdir.parseconfig(testdir.tmpdir) |         config = testdir.parseconfig(testdir.tmpdir) | ||||||
|  | @ -75,7 +83,7 @@ class TestCaptureManager: | ||||||
|     @needsosdup |     @needsosdup | ||||||
|     @pytest.mark.parametrize("method", ['no', 'fd', 'sys']) |     @pytest.mark.parametrize("method", ['no', 'fd', 'sys']) | ||||||
|     def test_capturing_basic_api(self, method): |     def test_capturing_basic_api(self, method): | ||||||
|         capouter = capture.StdCaptureFD() |         capouter = StdCaptureFD() | ||||||
|         old = sys.stdout, sys.stderr, sys.stdin |         old = sys.stdout, sys.stderr, sys.stdin | ||||||
|         try: |         try: | ||||||
|             capman = CaptureManager() |             capman = CaptureManager() | ||||||
|  | @ -95,11 +103,11 @@ class TestCaptureManager: | ||||||
|             assert not out and not err |             assert not out and not err | ||||||
|             capman.reset_capturings() |             capman.reset_capturings() | ||||||
|         finally: |         finally: | ||||||
|             capouter.reset() |             capouter.stop_capturing() | ||||||
| 
 | 
 | ||||||
|     @needsosdup |     @needsosdup | ||||||
|     def test_juggle_capturings(self, testdir): |     def test_juggle_capturings(self, testdir): | ||||||
|         capouter = capture.StdCaptureFD() |         capouter = StdCaptureFD() | ||||||
|         try: |         try: | ||||||
|             #config = testdir.parseconfig(testdir.tmpdir) |             #config = testdir.parseconfig(testdir.tmpdir) | ||||||
|             capman = CaptureManager() |             capman = CaptureManager() | ||||||
|  | @ -119,7 +127,7 @@ class TestCaptureManager: | ||||||
|             finally: |             finally: | ||||||
|                 capman.reset_capturings() |                 capman.reset_capturings() | ||||||
|         finally: |         finally: | ||||||
|             capouter.reset() |             capouter.stop_capturing() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.parametrize("method", ['fd', 'sys']) | @pytest.mark.parametrize("method", ['fd', 'sys']) | ||||||
|  | @ -282,9 +290,9 @@ class TestPerTestCapturing: | ||||||
|             "====* FAILURES *====", |             "====* FAILURES *====", | ||||||
|             "____*____", |             "____*____", | ||||||
|             "*test_capturing_outerr.py:8: ValueError", |             "*test_capturing_outerr.py:8: ValueError", | ||||||
|             "*--- Captured stdout ---*", |             "*--- Captured stdout *call*", | ||||||
|             "1", |             "1", | ||||||
|             "*--- Captured stderr ---*", |             "*--- Captured stderr *call*", | ||||||
|             "2", |             "2", | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|  | @ -688,17 +696,15 @@ class TestFDCapture: | ||||||
|         cap = capture.FDCapture(fd) |         cap = capture.FDCapture(fd) | ||||||
|         data = tobytes("hello") |         data = tobytes("hello") | ||||||
|         os.write(fd, data) |         os.write(fd, data) | ||||||
|         f = cap.done() |         s = cap.snap() | ||||||
|         s = f.read() |         cap.done() | ||||||
|         f.close() |  | ||||||
|         assert not s |         assert not s | ||||||
|         cap = capture.FDCapture(fd) |         cap = capture.FDCapture(fd) | ||||||
|         cap.start() |         cap.start() | ||||||
|         os.write(fd, data) |         os.write(fd, data) | ||||||
|         f = cap.done() |         s = cap.snap() | ||||||
|         s = f.read() |         cap.done() | ||||||
|         assert s == "hello" |         assert s == "hello" | ||||||
|         f.close() |  | ||||||
| 
 | 
 | ||||||
|     def test_simple_many(self, tmpfile): |     def test_simple_many(self, tmpfile): | ||||||
|         for i in range(10): |         for i in range(10): | ||||||
|  | @ -712,22 +718,21 @@ class TestFDCapture: | ||||||
|     def test_simple_fail_second_start(self, tmpfile): |     def test_simple_fail_second_start(self, tmpfile): | ||||||
|         fd = tmpfile.fileno() |         fd = tmpfile.fileno() | ||||||
|         cap = capture.FDCapture(fd) |         cap = capture.FDCapture(fd) | ||||||
|         f = cap.done() |         cap.done() | ||||||
|         pytest.raises(ValueError, cap.start) |         pytest.raises(ValueError, cap.start) | ||||||
|         f.close() |  | ||||||
| 
 | 
 | ||||||
|     def test_stderr(self): |     def test_stderr(self): | ||||||
|         cap = capture.FDCapture(2, patchsys=True) |         cap = capture.FDCapture(2) | ||||||
|         cap.start() |         cap.start() | ||||||
|         print_("hello", file=sys.stderr) |         print_("hello", file=sys.stderr) | ||||||
|         f = cap.done() |         s = cap.snap() | ||||||
|         s = f.read() |         cap.done() | ||||||
|         assert s == "hello\n" |         assert s == "hello\n" | ||||||
| 
 | 
 | ||||||
|     def test_stdin(self, tmpfile): |     def test_stdin(self, tmpfile): | ||||||
|         tmpfile.write(tobytes("3")) |         tmpfile.write(tobytes("3")) | ||||||
|         tmpfile.seek(0) |         tmpfile.seek(0) | ||||||
|         cap = capture.FDCapture(0, tmpfile=tmpfile) |         cap = capture.FDCapture(0, tmpfile) | ||||||
|         cap.start() |         cap.start() | ||||||
|         # check with os.read() directly instead of raw_input(), because |         # check with os.read() directly instead of raw_input(), because | ||||||
|         # sys.stdin itself may be redirected (as pytest now does by default) |         # sys.stdin itself may be redirected (as pytest now does by default) | ||||||
|  | @ -744,123 +749,121 @@ class TestFDCapture: | ||||||
|             cap.writeorg(data2) |             cap.writeorg(data2) | ||||||
|         finally: |         finally: | ||||||
|             tmpfile.close() |             tmpfile.close() | ||||||
|         f = cap.done() |         scap = cap.snap() | ||||||
|         scap = f.read() |         cap.done() | ||||||
|         assert scap == totext(data1) |         assert scap == totext(data1) | ||||||
|         stmp = open(tmpfile.name, 'rb').read() |         stmp = open(tmpfile.name, 'rb').read() | ||||||
|         assert stmp == data2 |         assert stmp == data2 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestStdCapture: | class TestStdCapture: | ||||||
|  |     captureclass = staticmethod(StdCapture) | ||||||
|  | 
 | ||||||
|  |     @contextlib.contextmanager | ||||||
|     def getcapture(self, **kw): |     def getcapture(self, **kw): | ||||||
|         cap = capture.StdCapture(**kw) |         cap = self.__class__.captureclass(**kw) | ||||||
|         cap.startall() |         cap.start_capturing() | ||||||
|         return cap |         try: | ||||||
|  |             yield cap | ||||||
|  |         finally: | ||||||
|  |             cap.stop_capturing() | ||||||
| 
 | 
 | ||||||
|     def test_capturing_done_simple(self): |     def test_capturing_done_simple(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         sys.stdout.write("hello") |             sys.stdout.write("hello") | ||||||
|         sys.stderr.write("world") |             sys.stderr.write("world") | ||||||
|         outfile, errfile = cap.done() |             out, err = cap.readouterr() | ||||||
|         s = outfile.read() |         assert out == "hello" | ||||||
|         assert s == "hello" |         assert err == "world" | ||||||
|         s = errfile.read() |  | ||||||
|         assert s == "world" |  | ||||||
| 
 | 
 | ||||||
|     def test_capturing_reset_simple(self): |     def test_capturing_reset_simple(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         print("hello world") |             print("hello world") | ||||||
|         sys.stderr.write("hello error\n") |             sys.stderr.write("hello error\n") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         assert out == "hello world\n" |         assert out == "hello world\n" | ||||||
|         assert err == "hello error\n" |         assert err == "hello error\n" | ||||||
| 
 | 
 | ||||||
|     def test_capturing_readouterr(self): |     def test_capturing_readouterr(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         try: |  | ||||||
|             print ("hello world") |             print ("hello world") | ||||||
|             sys.stderr.write("hello error\n") |             sys.stderr.write("hello error\n") | ||||||
|             out, err = cap.readouterr() |             out, err = cap.readouterr() | ||||||
|             assert out == "hello world\n" |             assert out == "hello world\n" | ||||||
|             assert err == "hello error\n" |             assert err == "hello error\n" | ||||||
|             sys.stderr.write("error2") |             sys.stderr.write("error2") | ||||||
|         finally: |             out, err = cap.readouterr() | ||||||
|             out, err = cap.reset() |  | ||||||
|         assert err == "error2" |         assert err == "error2" | ||||||
| 
 | 
 | ||||||
|     def test_capturing_readouterr_unicode(self): |     def test_capturing_readouterr_unicode(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         try: |  | ||||||
|             print ("hx\xc4\x85\xc4\x87") |             print ("hx\xc4\x85\xc4\x87") | ||||||
|             out, err = cap.readouterr() |             out, err = cap.readouterr() | ||||||
|         finally: |  | ||||||
|             cap.reset() |  | ||||||
|         assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") |         assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") | ||||||
| 
 | 
 | ||||||
|     @pytest.mark.skipif('sys.version_info >= (3,)', |     @pytest.mark.skipif('sys.version_info >= (3,)', | ||||||
|                         reason='text output different for bytes on python3') |                         reason='text output different for bytes on python3') | ||||||
|     def test_capturing_readouterr_decode_error_handling(self): |     def test_capturing_readouterr_decode_error_handling(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         # triggered a internal error in pytest |             # triggered a internal error in pytest | ||||||
|         print('\xa6') |             print('\xa6') | ||||||
|         out, err = cap.readouterr() |             out, err = cap.readouterr() | ||||||
|         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') |         assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') | ||||||
| 
 | 
 | ||||||
|     def test_reset_twice_error(self): |     def test_reset_twice_error(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         print ("hello") |             print ("hello") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         pytest.raises(ValueError, cap.reset) |         pytest.raises(ValueError, cap.stop_capturing) | ||||||
|         assert out == "hello\n" |         assert out == "hello\n" | ||||||
|         assert not err |         assert not err | ||||||
| 
 | 
 | ||||||
|     def test_capturing_modify_sysouterr_in_between(self): |     def test_capturing_modify_sysouterr_in_between(self): | ||||||
|         oldout = sys.stdout |         oldout = sys.stdout | ||||||
|         olderr = sys.stderr |         olderr = sys.stderr | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         sys.stdout.write("hello") |             sys.stdout.write("hello") | ||||||
|         sys.stderr.write("world") |             sys.stderr.write("world") | ||||||
|         sys.stdout = capture.TextIO() |             sys.stdout = capture.TextIO() | ||||||
|         sys.stderr = capture.TextIO() |             sys.stderr = capture.TextIO() | ||||||
|         print ("not seen") |             print ("not seen") | ||||||
|         sys.stderr.write("not seen\n") |             sys.stderr.write("not seen\n") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         assert out == "hello" |         assert out == "hello" | ||||||
|         assert err == "world" |         assert err == "world" | ||||||
|         assert sys.stdout == oldout |         assert sys.stdout == oldout | ||||||
|         assert sys.stderr == olderr |         assert sys.stderr == olderr | ||||||
| 
 | 
 | ||||||
|     def test_capturing_error_recursive(self): |     def test_capturing_error_recursive(self): | ||||||
|         cap1 = self.getcapture() |         with self.getcapture() as cap1: | ||||||
|         print ("cap1") |             print ("cap1") | ||||||
|         cap2 = self.getcapture() |             with self.getcapture() as cap2: | ||||||
|         print ("cap2") |                 print ("cap2") | ||||||
|         out2, err2 = cap2.reset() |                 out2, err2 = cap2.readouterr() | ||||||
|         out1, err1 = cap1.reset() |                 out1, err1 = cap1.readouterr() | ||||||
|         assert out1 == "cap1\n" |         assert out1 == "cap1\n" | ||||||
|         assert out2 == "cap2\n" |         assert out2 == "cap2\n" | ||||||
| 
 | 
 | ||||||
|     def test_just_out_capture(self): |     def test_just_out_capture(self): | ||||||
|         cap = self.getcapture(out=True, err=False) |         with self.getcapture(out=True, err=False) as cap: | ||||||
|         sys.stdout.write("hello") |             sys.stdout.write("hello") | ||||||
|         sys.stderr.write("world") |             sys.stderr.write("world") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         assert out == "hello" |         assert out == "hello" | ||||||
|         assert not err |         assert not err | ||||||
| 
 | 
 | ||||||
|     def test_just_err_capture(self): |     def test_just_err_capture(self): | ||||||
|         cap = self.getcapture(out=False, err=True) |         with self.getcapture(out=False, err=True) as cap: | ||||||
|         sys.stdout.write("hello") |             sys.stdout.write("hello") | ||||||
|         sys.stderr.write("world") |             sys.stderr.write("world") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         assert err == "world" |         assert err == "world" | ||||||
|         assert not out |         assert not out | ||||||
| 
 | 
 | ||||||
|     def test_stdin_restored(self): |     def test_stdin_restored(self): | ||||||
|         old = sys.stdin |         old = sys.stdin | ||||||
|         cap = self.getcapture(in_=True) |         with self.getcapture(in_=True) as cap: | ||||||
|         newstdin = sys.stdin |             newstdin = sys.stdin | ||||||
|         out, err = cap.reset() |  | ||||||
|         assert newstdin != sys.stdin |         assert newstdin != sys.stdin | ||||||
|         assert sys.stdin is old |         assert sys.stdin is old | ||||||
| 
 | 
 | ||||||
|  | @ -868,68 +871,47 @@ class TestStdCapture: | ||||||
|         print ("XXX this test may well hang instead of crashing") |         print ("XXX this test may well hang instead of crashing") | ||||||
|         print ("XXX which indicates an error in the underlying capturing") |         print ("XXX which indicates an error in the underlying capturing") | ||||||
|         print ("XXX mechanisms") |         print ("XXX mechanisms") | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         pytest.raises(IOError, "sys.stdin.read()") |             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 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestStdCaptureFD(TestStdCapture): | class TestStdCaptureFD(TestStdCapture): | ||||||
|     pytestmark = needsosdup |     pytestmark = needsosdup | ||||||
|  |     captureclass = staticmethod(StdCaptureFD) | ||||||
| 
 | 
 | ||||||
|     def getcapture(self, **kw): |     def test_simple_only_fd(self, testdir): | ||||||
|         cap = capture.StdCaptureFD(**kw) |         testdir.makepyfile(""" | ||||||
|         cap.startall() |             import os | ||||||
|         return cap |             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): |     def test_intermingling(self): | ||||||
|         cap = self.getcapture() |         with self.getcapture() as cap: | ||||||
|         oswritebytes(1, "1") |             oswritebytes(1, "1") | ||||||
|         sys.stdout.write(str(2)) |             sys.stdout.write(str(2)) | ||||||
|         sys.stdout.flush() |             sys.stdout.flush() | ||||||
|         oswritebytes(1, "3") |             oswritebytes(1, "3") | ||||||
|         oswritebytes(2, "a") |             oswritebytes(2, "a") | ||||||
|         sys.stderr.write("b") |             sys.stderr.write("b") | ||||||
|         sys.stderr.flush() |             sys.stderr.flush() | ||||||
|         oswritebytes(2, "c") |             oswritebytes(2, "c") | ||||||
|         out, err = cap.reset() |             out, err = cap.readouterr() | ||||||
|         assert out == "123" |         assert out == "123" | ||||||
|         assert err == "abc" |         assert err == "abc" | ||||||
| 
 | 
 | ||||||
|     def test_many(self, capfd): |     def test_many(self, capfd): | ||||||
|         with lsof_check(): |         with lsof_check(): | ||||||
|             for i in range(10): |             for i in range(10): | ||||||
|                 cap = capture.StdCaptureFD() |                 cap = StdCaptureFD() | ||||||
|                 cap.reset() |                 cap.stop_capturing() | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @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 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestStdCaptureFDinvalidFD: | class TestStdCaptureFDinvalidFD: | ||||||
|  | @ -938,19 +920,22 @@ class TestStdCaptureFDinvalidFD: | ||||||
|     def test_stdcapture_fd_invalid_fd(self, testdir): |     def test_stdcapture_fd_invalid_fd(self, testdir): | ||||||
|         testdir.makepyfile(""" |         testdir.makepyfile(""" | ||||||
|             import os |             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(): |             def test_stdout(): | ||||||
|                 os.close(1) |                 os.close(1) | ||||||
|                 cap = StdCaptureFD(out=True, err=False, in_=False) |                 cap = StdCaptureFD(out=True, err=False, in_=False) | ||||||
|                 cap.done() |                 cap.stop_capturing() | ||||||
|             def test_stderr(): |             def test_stderr(): | ||||||
|                 os.close(2) |                 os.close(2) | ||||||
|                 cap = StdCaptureFD(out=False, err=True, in_=False) |                 cap = StdCaptureFD(out=False, err=True, in_=False) | ||||||
|                 cap.done() |                 cap.stop_capturing() | ||||||
|             def test_stdin(): |             def test_stdin(): | ||||||
|                 os.close(0) |                 os.close(0) | ||||||
|                 cap = StdCaptureFD(out=False, err=False, in_=True) |                 cap = StdCaptureFD(out=False, err=False, in_=True) | ||||||
|                 cap.done() |                 cap.stop_capturing() | ||||||
|         """) |         """) | ||||||
|         result = testdir.runpytest("--capture=fd") |         result = testdir.runpytest("--capture=fd") | ||||||
|         assert result.ret == 0 |         assert result.ret == 0 | ||||||
|  | @ -958,27 +943,8 @@ class TestStdCaptureFDinvalidFD: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_capture_not_started_but_reset(): | def test_capture_not_started_but_reset(): | ||||||
|     capsys = capture.StdCapture() |     capsys = StdCapture() | ||||||
|     capsys.done() |     capsys.stop_capturing() | ||||||
|     capsys.done() |  | ||||||
|     capsys.reset() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @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() |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @needsosdup | @needsosdup | ||||||
|  | @ -986,19 +952,18 @@ def test_capture_no_sys(): | ||||||
| def test_fdcapture_tmpfile_remains_the_same(tmpfile, use): | def test_fdcapture_tmpfile_remains_the_same(tmpfile, use): | ||||||
|     if not use: |     if not use: | ||||||
|         tmpfile = True |         tmpfile = True | ||||||
|     cap = capture.StdCaptureFD(out=False, err=tmpfile) |     cap = StdCaptureFD(out=False, err=tmpfile) | ||||||
|     try: |     try: | ||||||
|         cap.startall() |         cap.start_capturing() | ||||||
|         capfile = cap.err.tmpfile |         capfile = cap.err.tmpfile | ||||||
|         cap.suspend() |         cap.readouterr() | ||||||
|         cap.resume() |  | ||||||
|     finally: |     finally: | ||||||
|         cap.reset() |         cap.stop_capturing() | ||||||
|     capfile2 = cap.err.tmpfile |     capfile2 = cap.err.tmpfile | ||||||
|     assert capfile2 == capfile |     assert capfile2 == capfile | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.parametrize('method', ['StdCapture', 'StdCaptureFD']) | @pytest.mark.parametrize('method', ['SysCapture', 'FDCapture']) | ||||||
| def test_capturing_and_logging_fundamentals(testdir, method): | def test_capturing_and_logging_fundamentals(testdir, method): | ||||||
|     if method == "StdCaptureFD" and not hasattr(os, 'dup'): |     if method == "StdCaptureFD" and not hasattr(os, 'dup'): | ||||||
|         pytest.skip("need os.dup") |         pytest.skip("need os.dup") | ||||||
|  | @ -1007,23 +972,27 @@ def test_capturing_and_logging_fundamentals(testdir, method): | ||||||
|         import sys, os |         import sys, os | ||||||
|         import py, logging |         import py, logging | ||||||
|         from _pytest import capture |         from _pytest import capture | ||||||
|         cap = capture.%s(out=False, in_=False) |         cap = capture.StdCaptureBase(out=False, in_=False, | ||||||
|         cap.startall() |                                      Capture=capture.%s) | ||||||
|  |         cap.start_capturing() | ||||||
| 
 | 
 | ||||||
|         logging.warn("hello1") |         logging.warn("hello1") | ||||||
|         outerr = cap.suspend() |         outerr = cap.readouterr() | ||||||
|         print ("suspend, captured %%s" %%(outerr,)) |         print ("suspend, captured %%s" %%(outerr,)) | ||||||
|         logging.warn("hello2") |         logging.warn("hello2") | ||||||
| 
 | 
 | ||||||
|         cap.resume() |         cap.pop_outerr_to_orig() | ||||||
|         logging.warn("hello3") |         logging.warn("hello3") | ||||||
| 
 | 
 | ||||||
|         outerr = cap.suspend() |         outerr = cap.readouterr() | ||||||
|         print ("suspend2, captured %%s" %% (outerr,)) |         print ("suspend2, captured %%s" %% (outerr,)) | ||||||
|     """ % (method,)) |     """ % (method,)) | ||||||
|     result = testdir.runpython(p) |     result = testdir.runpython(p) | ||||||
|     result.stdout.fnmatch_lines([ |     result.stdout.fnmatch_lines(""" | ||||||
|         "suspend, captured*hello1*", |         suspend, captured*hello1* | ||||||
|         "suspend2, captured*hello2*WARNING:root:hello3*", |         suspend2, captured*WARNING:root:hello3* | ||||||
|     ]) |     """) | ||||||
|  |     result.stderr.fnmatch_lines(""" | ||||||
|  |         WARNING:root:hello2 | ||||||
|  |     """) | ||||||
|     assert "atexit" not in result.stderr.str() |     assert "atexit" not in result.stderr.str() | ||||||
|  |  | ||||||
|  | @ -523,6 +523,95 @@ class TestMultiCall: | ||||||
|         res = MultiCall([m1, m2], {}).execute() |         res = MultiCall([m1, m2], {}).execute() | ||||||
|         assert res == [1] |         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: | class TestHookRelay: | ||||||
|     def test_happypath(self): |     def test_happypath(self): | ||||||
|         pm = PluginManager() |         pm = PluginManager() | ||||||
|  |  | ||||||
|  | @ -478,10 +478,12 @@ def test_unicode_issue368(testdir): | ||||||
|     path = testdir.tmpdir.join("test.xml") |     path = testdir.tmpdir.join("test.xml") | ||||||
|     log = LogXML(str(path), None) |     log = LogXML(str(path), None) | ||||||
|     ustr = py.builtin._totext("ВНИ!", "utf-8") |     ustr = py.builtin._totext("ВНИ!", "utf-8") | ||||||
|     class report: |     from _pytest.runner import BaseReport | ||||||
|  |     class Report(BaseReport): | ||||||
|         longrepr = ustr |         longrepr = ustr | ||||||
|         sections = [] |         sections = [] | ||||||
|         nodeid = "something" |         nodeid = "something" | ||||||
|  |     report = Report() | ||||||
| 
 | 
 | ||||||
|     # hopefully this is not too brittle ... |     # hopefully this is not too brittle ... | ||||||
|     log.pytest_sessionstart() |     log.pytest_sessionstart() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue