fix issue96 - make capturing more resilient against KeyboardInterrupt
--HG-- branch : trunk
This commit is contained in:
		
							parent
							
								
									5876736890
								
							
						
					
					
						commit
						e71685736e
					
				|  | @ -1,6 +1,11 @@ | ||||||
| Changes between 1.3.0 and 1.3.1 | Changes between 1.3.0 and 1.3.1 | ||||||
| ================================================== | ================================================== | ||||||
| 
 | 
 | ||||||
|  | - fix issue96: make capturing more resilient against Control-C  | ||||||
|  |   interruptions (involved somewhat substantial refactoring | ||||||
|  |   to the underlying capturing functionality to avoid race  | ||||||
|  |   conditions). | ||||||
|  | 
 | ||||||
| - make py.test.cmdline.main() return the exitstatus  | - make py.test.cmdline.main() return the exitstatus  | ||||||
|   instead of raising (which is still done by py.cmdline.pytest()) |   instead of raising (which is still done by py.cmdline.pytest()) | ||||||
|   and make it so that py.test.cmdline.main() can be called |   and make it so that py.test.cmdline.main() can be called | ||||||
|  |  | ||||||
|  | @ -29,21 +29,25 @@ except ImportError: | ||||||
| class FDCapture:  | class FDCapture:  | ||||||
|     """ Capture IO to/from a given os-level filedescriptor. """ |     """ Capture IO to/from a given os-level filedescriptor. """ | ||||||
|      |      | ||||||
|     def __init__(self, targetfd, tmpfile=None):  |     def __init__(self, targetfd, tmpfile=None, now=True): | ||||||
|         """ save targetfd descriptor, and open a new  |         """ save targetfd descriptor, and open a new  | ||||||
|             temporary file there.  If no tmpfile is  |             temporary file there.  If no tmpfile is  | ||||||
|             specified a tempfile.Tempfile() will be opened |             specified a tempfile.Tempfile() will be opened | ||||||
|             in text mode.  |             in text mode.  | ||||||
|         """ |         """ | ||||||
|         self.targetfd = targetfd |         self.targetfd = targetfd | ||||||
|  |         self._patched = [] | ||||||
|         if tmpfile is None:  |         if tmpfile is None:  | ||||||
|             f = tempfile.TemporaryFile('wb+') |             f = tempfile.TemporaryFile('wb+') | ||||||
|             tmpfile = dupfile(f, encoding="UTF-8")  |             tmpfile = dupfile(f, encoding="UTF-8")  | ||||||
|             f.close() |             f.close() | ||||||
|         self.tmpfile = tmpfile  |         self.tmpfile = tmpfile  | ||||||
|         self._savefd = os.dup(targetfd) |         if now: | ||||||
|         os.dup2(self.tmpfile.fileno(), targetfd)  |             self.start() | ||||||
|         self._patched = [] | 
 | ||||||
|  |     def start(self): | ||||||
|  |         self._savefd = os.dup(self.targetfd) | ||||||
|  |         os.dup2(self.tmpfile.fileno(), self.targetfd)  | ||||||
| 
 | 
 | ||||||
|     def setasfile(self, name, module=sys):  |     def setasfile(self, name, module=sys):  | ||||||
|         """ patch <module>.<name> to self.tmpfile |         """ patch <module>.<name> to self.tmpfile | ||||||
|  | @ -62,10 +66,13 @@ class FDCapture: | ||||||
|     def done(self):  |     def done(self):  | ||||||
|         """ unpatch and clean up, returns the self.tmpfile (file object) |         """ unpatch and clean up, returns the self.tmpfile (file object) | ||||||
|         """ |         """ | ||||||
|  |         try: | ||||||
|             os.dup2(self._savefd, self.targetfd)  |             os.dup2(self._savefd, self.targetfd)  | ||||||
|         self.unsetfiles()  |  | ||||||
|             os.close(self._savefd)  |             os.close(self._savefd)  | ||||||
|             self.tmpfile.seek(0) |             self.tmpfile.seek(0) | ||||||
|  |         except (AttributeError, ValueError, OSError): | ||||||
|  |             pass | ||||||
|  |         self.unsetfiles()  | ||||||
|         return self.tmpfile  |         return self.tmpfile  | ||||||
| 
 | 
 | ||||||
|     def writeorg(self, data): |     def writeorg(self, data): | ||||||
|  | @ -146,89 +153,92 @@ class Capture(object): | ||||||
| 
 | 
 | ||||||
|     def reset(self): |     def reset(self): | ||||||
|         """ reset sys.stdout/stderr and return captured output as strings. """ |         """ reset sys.stdout/stderr and return captured output as strings. """ | ||||||
|         if hasattr(self, '_suspended'): |  | ||||||
|             outfile = self._kwargs['out'] |  | ||||||
|             errfile = self._kwargs['err'] |  | ||||||
|             del self._kwargs |  | ||||||
|         else: |  | ||||||
|         outfile, errfile = self.done()  |         outfile, errfile = self.done()  | ||||||
|         out, err = "", "" |         out, err = "", "" | ||||||
|         if outfile: |         if outfile and not outfile.closed: | ||||||
|             out = outfile.read() |             out = outfile.read() | ||||||
|             outfile.close() |             outfile.close() | ||||||
|         if errfile and errfile != outfile: |         if errfile and errfile != outfile and not errfile.closed: | ||||||
|             err = errfile.read() |             err = errfile.read() | ||||||
|             errfile.close() |             errfile.close() | ||||||
|         return out, err |         return out, err | ||||||
| 
 | 
 | ||||||
|     def suspend(self): |     def suspend(self): | ||||||
|         """ return current snapshot captures, memorize tempfiles. """ |         """ return current snapshot captures, memorize tempfiles. """ | ||||||
|         assert not hasattr(self, '_suspended') |  | ||||||
|         self._suspended = True |  | ||||||
|         outerr = self.readouterr() |         outerr = self.readouterr() | ||||||
|         outfile, errfile = self.done() |         outfile, errfile = self.done() | ||||||
|         self._kwargs['out'] = outfile |  | ||||||
|         self._kwargs['err'] = errfile |  | ||||||
|         return outerr |         return outerr | ||||||
| 
 | 
 | ||||||
|     def resume(self): |  | ||||||
|         """ resume capturing with original temp files. """ |  | ||||||
|         assert self._suspended |  | ||||||
|         self._initialize(**self._kwargs) |  | ||||||
|         del self._suspended |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class StdCaptureFD(Capture):  | class StdCaptureFD(Capture):  | ||||||
|     """ This class allows to capture writes to FD1 and FD2  |     """ This class allows to capture writes to FD1 and FD2  | ||||||
|         and may connect a NULL file to FD0 (and prevent |         and may connect a NULL file to FD0 (and prevent | ||||||
|         reads from sys.stdin) |         reads from sys.stdin) | ||||||
|     """ |     """ | ||||||
|     def __init__(self, out=True, err=True,  |     def __init__(self, out=True, err=True, mixed=False,  | ||||||
|                  mixed=False, in_=True, patchsys=True):  |         in_=True, patchsys=True, now=True): | ||||||
|         self._kwargs = locals().copy() |         self.in_ = in_ | ||||||
|         del self._kwargs['self'] |  | ||||||
|         self._initialize(**self._kwargs) |  | ||||||
| 
 |  | ||||||
|     def _initialize(self, out=True, err=True,  |  | ||||||
|                     mixed=False, in_=True, patchsys=True):  |  | ||||||
|         if in_: |         if in_: | ||||||
|             self._oldin = (sys.stdin, os.dup(0)) |             self._oldin = (sys.stdin, os.dup(0)) | ||||||
|             sys.stdin  = DontReadFromInput() |  | ||||||
|             fd = os.open(devnullpath, os.O_RDONLY) |  | ||||||
|             os.dup2(fd, 0) |  | ||||||
|             os.close(fd) |  | ||||||
|         if out: |         if out: | ||||||
|             tmpfile = None |             tmpfile = None | ||||||
|             if hasattr(out, 'write'): |             if hasattr(out, 'write'): | ||||||
|                 tmpfile = out |                 tmpfile = None | ||||||
|             self.out = py.io.FDCapture(1, tmpfile=tmpfile) |             self.out = py.io.FDCapture(1, tmpfile=tmpfile, now=False) | ||||||
|             if patchsys:  |             self.out_tmpfile = tmpfile | ||||||
|                 self.out.setasfile('stdout') |  | ||||||
|         if err: |         if err: | ||||||
|             if mixed and out: |             if out and mixed: | ||||||
|                 tmpfile = self.out.tmpfile  |                 tmpfile = self.out.tmpfile  | ||||||
|             elif hasattr(err, 'write'): |             elif hasattr(err, 'write'): | ||||||
|                 tmpfile = err |                 tmpfile = err | ||||||
|             else: |             else: | ||||||
|                 tmpfile = None |                 tmpfile = None | ||||||
|             self.err = py.io.FDCapture(2, tmpfile=tmpfile)  |             self.err = py.io.FDCapture(2, tmpfile=tmpfile, now=False)  | ||||||
|             if patchsys:  |             self.err_tmpfile = tmpfile | ||||||
|                 self.err.setasfile('stderr') |         self.patchsys = patchsys | ||||||
|  |         if now: | ||||||
|  |             self.startall() | ||||||
|  | 
 | ||||||
|  |     def startall(self): | ||||||
|  |         if self.in_: | ||||||
|  |             sys.stdin  = DontReadFromInput() | ||||||
|  |             fd = os.open(devnullpath, os.O_RDONLY) | ||||||
|  |             os.dup2(fd, 0) | ||||||
|  |             os.close(fd) | ||||||
|  |         | ||||||
|  |         out = getattr(self, 'out', None)  | ||||||
|  |         if out: | ||||||
|  |             out.start() | ||||||
|  |             if self.patchsys: | ||||||
|  |                 out.setasfile('stdout') | ||||||
|  |         err = getattr(self, 'err', None) | ||||||
|  |         if err: | ||||||
|  |             err.start() | ||||||
|  |             if self.patchsys: | ||||||
|  |                 err.setasfile('stderr') | ||||||
|  | 
 | ||||||
|  |     def resume(self): | ||||||
|  |         """ resume capturing with original temp files. """ | ||||||
|  |         #if hasattr(self, 'out'): | ||||||
|  |         #    self.out.restart() | ||||||
|  |         #if hasattr(self, 'err'): | ||||||
|  |         #    self.err.restart() | ||||||
|  |         self.startall() | ||||||
| 
 | 
 | ||||||
|     def done(self): |     def done(self): | ||||||
|         """ return (outfile, errfile) and stop capturing. """ |         """ return (outfile, errfile) and stop capturing. """ | ||||||
|  |         outfile = errfile = None | ||||||
|         if hasattr(self, 'out'):  |         if hasattr(self, 'out'):  | ||||||
|             outfile = self.out.done()  |             outfile = self.out.done()  | ||||||
|         else: |  | ||||||
|             outfile = None |  | ||||||
|         if hasattr(self, 'err'):  |         if hasattr(self, 'err'):  | ||||||
|             errfile = self.err.done()  |             errfile = self.err.done()  | ||||||
|         else: |  | ||||||
|             errfile = None  |  | ||||||
|         if hasattr(self, '_oldin'): |         if hasattr(self, '_oldin'): | ||||||
|             oldsys, oldfd = self._oldin  |             oldsys, oldfd = self._oldin  | ||||||
|  |             try: | ||||||
|                 os.dup2(oldfd, 0) |                 os.dup2(oldfd, 0) | ||||||
|                 os.close(oldfd) |                 os.close(oldfd) | ||||||
|  |             except OSError: | ||||||
|  |                 pass | ||||||
|             sys.stdin = oldsys  |             sys.stdin = oldsys  | ||||||
|         return outfile, errfile  |         return outfile, errfile  | ||||||
| 
 | 
 | ||||||
|  | @ -252,69 +262,61 @@ class StdCapture(Capture): | ||||||
|         modifies sys.stdout|stderr|stdin attributes and does not  |         modifies sys.stdout|stderr|stdin attributes and does not  | ||||||
|         touch underlying File Descriptors (use StdCaptureFD for that).  |         touch underlying File Descriptors (use StdCaptureFD for that).  | ||||||
|     """ |     """ | ||||||
|     def __init__(self, out=True, err=True, in_=True, mixed=False): |     def __init__(self, out=True, err=True, in_=True, mixed=False, now=True): | ||||||
|         self._kwargs = locals().copy() |  | ||||||
|         del self._kwargs['self'] |  | ||||||
|         self._initialize(**self._kwargs) |  | ||||||
| 
 |  | ||||||
|     def _initialize(self, out, err, in_, mixed): |  | ||||||
|         self._out = out |  | ||||||
|         self._err = err  |  | ||||||
|         self._in = in_ |  | ||||||
|         if out:  |  | ||||||
|         self._oldout = sys.stdout |         self._oldout = sys.stdout | ||||||
|             if not hasattr(out, 'write'): |  | ||||||
|                 out = TextIO() |  | ||||||
|             sys.stdout = self.out = out |  | ||||||
|         if err:  |  | ||||||
|         self._olderr = sys.stderr |         self._olderr = sys.stderr | ||||||
|             if out and mixed:  |         self._oldin  = sys.stdin | ||||||
|                 err = self.out  |         if out and not hasattr(out, 'file'): | ||||||
|  |             out = TextIO() | ||||||
|  |         self.out = out | ||||||
|  |         if err: | ||||||
|  |             if mixed: | ||||||
|  |                 err = out | ||||||
|             elif not hasattr(err, 'write'): |             elif not hasattr(err, 'write'): | ||||||
|                 err = TextIO() |                 err = TextIO() | ||||||
|             sys.stderr = self.err = err |         self.err = err | ||||||
|         if in_: |         self.in_ = in_ | ||||||
|             self._oldin  = sys.stdin |         if now: | ||||||
|             sys.stdin  = self.newin  = DontReadFromInput() |             self.startall() | ||||||
|  | 
 | ||||||
|  |     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):  |     def done(self):  | ||||||
|         """ return (outfile, errfile) and stop capturing. """ |         """ return (outfile, errfile) and stop capturing. """ | ||||||
|         o,e = sys.stdout, sys.stderr |         outfile = errfile = None | ||||||
|         if self._out:  |         if self.out and not self.out.closed: | ||||||
|             try: |  | ||||||
|             sys.stdout = self._oldout  |             sys.stdout = self._oldout  | ||||||
|             except AttributeError: |  | ||||||
|                 raise IOError("stdout capturing already reset") |  | ||||||
|             del self._oldout |  | ||||||
|             outfile = self.out |             outfile = self.out | ||||||
|             outfile.seek(0) |             outfile.seek(0) | ||||||
|         else: |         if self.err and not self.err.closed:  | ||||||
|             outfile = None |  | ||||||
|         if self._err:  |  | ||||||
|             try: |  | ||||||
|             sys.stderr = self._olderr  |             sys.stderr = self._olderr  | ||||||
|             except AttributeError: |  | ||||||
|                 raise IOError("stderr capturing already reset") |  | ||||||
|             del self._olderr  |  | ||||||
|             errfile = self.err  |             errfile = self.err  | ||||||
|             errfile.seek(0) |             errfile.seek(0) | ||||||
|         else: |         if self.in_: | ||||||
|             errfile = None |  | ||||||
|         if self._in: |  | ||||||
|             sys.stdin = self._oldin  |             sys.stdin = self._oldin  | ||||||
|         return outfile, errfile |         return outfile, errfile | ||||||
| 
 | 
 | ||||||
|  |     def resume(self): | ||||||
|  |         """ resume capturing with original temp files. """ | ||||||
|  |         self.startall() | ||||||
|  | 
 | ||||||
|     def readouterr(self): |     def readouterr(self): | ||||||
|         """ return snapshot value of stdout/stderr capturings. """ |         """ return snapshot value of stdout/stderr capturings. """ | ||||||
|         out = err = "" |         out = err = "" | ||||||
|         if self._out: |         if self.out: | ||||||
|             out = sys.stdout.getvalue() |             out = self.out.getvalue() | ||||||
|             sys.stdout.truncate(0) |             self.out.truncate(0) | ||||||
|             sys.stdout.seek(0) |             self.out.seek(0) | ||||||
|         if self._err: |         if self.err: | ||||||
|             err = sys.stderr.getvalue() |             err = self.err.getvalue() | ||||||
|             sys.stderr.truncate(0) |             self.err.truncate(0) | ||||||
|             sys.stderr.seek(0) |             self.err.seek(0) | ||||||
|         return out, err  |         return out, err  | ||||||
| 
 | 
 | ||||||
| class DontReadFromInput: | class DontReadFromInput: | ||||||
|  | @ -344,5 +346,3 @@ except AttributeError: | ||||||
|         devnullpath = 'NUL' |         devnullpath = 'NUL' | ||||||
|     else: |     else: | ||||||
|         devnullpath = '/dev/null' |         devnullpath = '/dev/null' | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -106,6 +106,14 @@ def addouterr(rep, outerr): | ||||||
| def pytest_configure(config): | def pytest_configure(config): | ||||||
|     config.pluginmanager.register(CaptureManager(), 'capturemanager') |     config.pluginmanager.register(CaptureManager(), 'capturemanager') | ||||||
| 
 | 
 | ||||||
|  | class NoCapture: | ||||||
|  |     def startall(self): | ||||||
|  |         pass | ||||||
|  |     def resume(self): | ||||||
|  |         pass | ||||||
|  |     def suspend(self): | ||||||
|  |         return "", "" | ||||||
|  | 
 | ||||||
| class CaptureManager: | class CaptureManager: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._method2capture = {} |         self._method2capture = {} | ||||||
|  | @ -118,15 +126,17 @@ class CaptureManager: | ||||||
|     def _makestringio(self): |     def _makestringio(self): | ||||||
|         return py.io.TextIO()  |         return py.io.TextIO()  | ||||||
| 
 | 
 | ||||||
|     def _startcapture(self, method): |     def _getcapture(self, method): | ||||||
|         if method == "fd":  |         if method == "fd":  | ||||||
|             return py.io.StdCaptureFD( |             return py.io.StdCaptureFD(now=False, | ||||||
|                 out=self._maketempfile(), err=self._maketempfile() |                 out=self._maketempfile(), err=self._maketempfile() | ||||||
|             ) |             ) | ||||||
|         elif method == "sys": |         elif method == "sys": | ||||||
|             return py.io.StdCapture( |             return py.io.StdCapture(now=False, | ||||||
|                 out=self._makestringio(), err=self._makestringio() |                 out=self._makestringio(), err=self._makestringio() | ||||||
|             ) |             ) | ||||||
|  |         elif method == "no": | ||||||
|  |             return NoCapture() | ||||||
|         else: |         else: | ||||||
|             raise ValueError("unknown capturing method: %r" % method) |             raise ValueError("unknown capturing method: %r" % method) | ||||||
| 
 | 
 | ||||||
|  | @ -152,27 +162,25 @@ class CaptureManager: | ||||||
|         if hasattr(self, '_capturing'): |         if hasattr(self, '_capturing'): | ||||||
|             raise ValueError("cannot resume, already capturing with %r" %  |             raise ValueError("cannot resume, already capturing with %r" %  | ||||||
|                 (self._capturing,)) |                 (self._capturing,)) | ||||||
|         if method != "no": |  | ||||||
|         cap = self._method2capture.get(method) |         cap = self._method2capture.get(method) | ||||||
|  |         self._capturing = method  | ||||||
|         if cap is None: |         if cap is None: | ||||||
|                 cap = self._startcapture(method) |             self._method2capture[method] = cap = self._getcapture(method) | ||||||
|                 self._method2capture[method] = cap  |             cap.startall() | ||||||
|         else: |         else: | ||||||
|             cap.resume() |             cap.resume() | ||||||
|         self._capturing = method  |  | ||||||
| 
 | 
 | ||||||
|     def suspendcapture(self, item=None): |     def suspendcapture(self, item=None): | ||||||
|         self.deactivate_funcargs() |         self.deactivate_funcargs() | ||||||
|         if hasattr(self, '_capturing'): |         if hasattr(self, '_capturing'): | ||||||
|             method = self._capturing |             method = self._capturing | ||||||
|             if method != "no": |             cap = self._method2capture.get(method) | ||||||
|                 cap = self._method2capture[method] |             if cap is not None: | ||||||
|                 outerr = cap.suspend() |                 outerr = cap.suspend() | ||||||
|             else: |  | ||||||
|                 outerr = "", "" |  | ||||||
|             del self._capturing |             del self._capturing | ||||||
|             if item: |             if item: | ||||||
|                 outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1]) |                 outerr = (item.outerr[0] + outerr[0],  | ||||||
|  |                           item.outerr[1] + outerr[1]) | ||||||
|             return outerr  |             return outerr  | ||||||
|         return "", "" |         return "", "" | ||||||
| 
 | 
 | ||||||
|  | @ -180,19 +188,17 @@ class CaptureManager: | ||||||
|         if not hasattr(pyfuncitem, 'funcargs'): |         if not hasattr(pyfuncitem, 'funcargs'): | ||||||
|             return |             return | ||||||
|         assert not hasattr(self, '_capturing_funcargs') |         assert not hasattr(self, '_capturing_funcargs') | ||||||
|         l = [] |         self._capturing_funcargs = capturing_funcargs = [] | ||||||
|         for name, obj in pyfuncitem.funcargs.items(): |         for name, capfuncarg in pyfuncitem.funcargs.items(): | ||||||
|             if name == 'capfd' and not hasattr(os, 'dup'): |  | ||||||
|                 py.test.skip("capfd funcarg needs os.dup") |  | ||||||
|             if name in ('capsys', 'capfd'): |             if name in ('capsys', 'capfd'): | ||||||
|                 obj._start() |                 capturing_funcargs.append(capfuncarg) | ||||||
|                 l.append(obj) |                 capfuncarg._start() | ||||||
|         if l: |  | ||||||
|             self._capturing_funcargs = l |  | ||||||
| 
 | 
 | ||||||
|     def deactivate_funcargs(self): |     def deactivate_funcargs(self): | ||||||
|         if hasattr(self, '_capturing_funcargs'): |         capturing_funcargs = getattr(self, '_capturing_funcargs', None) | ||||||
|             for capfuncarg in self._capturing_funcargs: |         if capturing_funcargs is not None: | ||||||
|  |             while capturing_funcargs: | ||||||
|  |                 capfuncarg = capturing_funcargs.pop() | ||||||
|                 capfuncarg._finalize() |                 capfuncarg._finalize() | ||||||
|             del self._capturing_funcargs |             del self._capturing_funcargs | ||||||
| 
 | 
 | ||||||
|  | @ -256,16 +262,19 @@ def pytest_funcarg__capfd(request): | ||||||
|     platform does not have ``os.dup`` (e.g. Jython) tests using |     platform does not have ``os.dup`` (e.g. Jython) tests using | ||||||
|     this funcarg will automatically skip.  |     this funcarg will automatically skip.  | ||||||
|     """  |     """  | ||||||
|  |     if not hasattr(os, 'dup'): | ||||||
|  |         py.test.skip("capfd funcarg needs os.dup") | ||||||
|     return CaptureFuncarg(request, py.io.StdCaptureFD) |     return CaptureFuncarg(request, py.io.StdCaptureFD) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CaptureFuncarg: | class CaptureFuncarg: | ||||||
|     def __init__(self, request, captureclass): |     def __init__(self, request, captureclass): | ||||||
|         self._cclass = captureclass |         self._cclass = captureclass | ||||||
|  |         self.capture = self._cclass(now=False) | ||||||
|         #request.addfinalizer(self._finalize) |         #request.addfinalizer(self._finalize) | ||||||
| 
 | 
 | ||||||
|     def _start(self): |     def _start(self): | ||||||
|         self.capture = self._cclass() |         self.capture.startall() | ||||||
| 
 | 
 | ||||||
|     def _finalize(self): |     def _finalize(self): | ||||||
|         if hasattr(self, 'capture'): |         if hasattr(self, 'capture'): | ||||||
|  | @ -276,6 +285,4 @@ class CaptureFuncarg: | ||||||
|         return self.capture.readouterr() |         return self.capture.readouterr() | ||||||
| 
 | 
 | ||||||
|     def close(self): |     def close(self): | ||||||
|         self.capture.reset() |         self._finalize() | ||||||
|         del self.capture |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -96,6 +96,21 @@ def test_dupfile(tmpfile): | ||||||
| class TestFDCapture:  | class TestFDCapture:  | ||||||
|     pytestmark = needsdup  |     pytestmark = needsdup  | ||||||
| 
 | 
 | ||||||
|  |     def test_not_now(self, tmpfile): | ||||||
|  |         fd = tmpfile.fileno() | ||||||
|  |         cap = py.io.FDCapture(fd, now=False) | ||||||
|  |         data = tobytes("hello") | ||||||
|  |         os.write(fd, data) | ||||||
|  |         f = cap.done() | ||||||
|  |         s = f.read() | ||||||
|  |         assert not s  | ||||||
|  |         cap = py.io.FDCapture(fd, now=False) | ||||||
|  |         cap.start() | ||||||
|  |         os.write(fd, data) | ||||||
|  |         f = cap.done() | ||||||
|  |         s = f.read() | ||||||
|  |         assert s == "hello" | ||||||
|  | 
 | ||||||
|     def test_stdout(self, tmpfile): |     def test_stdout(self, tmpfile): | ||||||
|         fd = tmpfile.fileno() |         fd = tmpfile.fileno() | ||||||
|         cap = py.io.FDCapture(fd) |         cap = py.io.FDCapture(fd) | ||||||
|  | @ -185,8 +200,11 @@ class TestStdCapture: | ||||||
|     def test_capturing_twice_error(self): |     def test_capturing_twice_error(self): | ||||||
|         cap = self.getcapture()  |         cap = self.getcapture()  | ||||||
|         print ("hello") |         print ("hello") | ||||||
|         cap.reset() |         out, err = cap.reset() | ||||||
|         py.test.raises(Exception, "cap.reset()") |         print ("world") | ||||||
|  |         out2, err = cap.reset() | ||||||
|  |         assert out == "hello\n" | ||||||
|  |         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  | ||||||
|  | @ -210,7 +228,6 @@ class TestStdCapture: | ||||||
|         cap2 = self.getcapture()  |         cap2 = self.getcapture()  | ||||||
|         print ("cap2") |         print ("cap2") | ||||||
|         out2, err2 = cap2.reset() |         out2, err2 = cap2.reset() | ||||||
|         py.test.raises(Exception, "cap2.reset()") |  | ||||||
|         out1, err1 = cap1.reset()  |         out1, err1 = cap1.reset()  | ||||||
|         assert out1 == "cap1\n" |         assert out1 == "cap1\n" | ||||||
|         assert out2 == "cap2\n" |         assert out2 == "cap2\n" | ||||||
|  | @ -265,6 +282,13 @@ class TestStdCapture: | ||||||
|         assert out == "after\n" |         assert out == "after\n" | ||||||
|         assert not err  |         assert not err  | ||||||
| 
 | 
 | ||||||
|  | class TestStdCaptureNotNow(TestStdCapture): | ||||||
|  |     def getcapture(self, **kw): | ||||||
|  |         kw['now'] = False | ||||||
|  |         cap = py.io.StdCapture(**kw) | ||||||
|  |         cap.startall() | ||||||
|  |         return cap | ||||||
|  | 
 | ||||||
| class TestStdCaptureFD(TestStdCapture):  | class TestStdCaptureFD(TestStdCapture):  | ||||||
|     pytestmark = needsdup |     pytestmark = needsdup | ||||||
| 
 | 
 | ||||||
|  | @ -296,6 +320,22 @@ class TestStdCaptureFD(TestStdCapture): | ||||||
|         assert out.startswith("3")  |         assert out.startswith("3")  | ||||||
|         assert err.startswith("4")  |         assert err.startswith("4")  | ||||||
| 
 | 
 | ||||||
|  | class TestStdCaptureFDNotNow(TestStdCaptureFD): | ||||||
|  |     pytestmark = needsdup | ||||||
|  | 
 | ||||||
|  |     def getcapture(self, **kw):  | ||||||
|  |         kw['now'] = False | ||||||
|  |         cap = py.io.StdCaptureFD(**kw) | ||||||
|  |         cap.startall() | ||||||
|  |         return cap | ||||||
|  | 
 | ||||||
|  | def test_capture_not_started_but_reset():  | ||||||
|  |     capsys = py.io.StdCapture(now=False) | ||||||
|  |     capsys.done() | ||||||
|  |     capsys.done() | ||||||
|  |     capsys.reset() | ||||||
|  |     capsys.reset() | ||||||
|  | 
 | ||||||
| @needsdup | @needsdup | ||||||
| def test_capture_no_sys():  | def test_capture_no_sys():  | ||||||
|     capsys = py.io.StdCapture() |     capsys = py.io.StdCapture() | ||||||
|  |  | ||||||
|  | @ -39,6 +39,10 @@ class TestCaptureManager: | ||||||
|         old = sys.stdout, sys.stderr, sys.stdin |         old = sys.stdout, sys.stderr, sys.stdin | ||||||
|         try: |         try: | ||||||
|             capman = CaptureManager() |             capman = CaptureManager() | ||||||
|  |             # call suspend without resume or start | ||||||
|  |             outerr = capman.suspendcapture() | ||||||
|  |             outerr = capman.suspendcapture() | ||||||
|  |             assert outerr == ("", "") | ||||||
|             capman.resumecapture(method) |             capman.resumecapture(method) | ||||||
|             print ("hello") |             print ("hello") | ||||||
|             out, err = capman.suspendcapture() |             out, err = capman.suspendcapture() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue