From issues-reply at bitbucket.org Tue Apr 1 11:32:43 2014 From: issues-reply at bitbucket.org (=?utf-8?q?Thomas_G=C3=BCttler?=) Date: Tue, 01 Apr 2014 09:32:43 -0000 Subject: [Pytest-commit] Issue #495: Docs search: setup.py should return a result (hpk42/pytest) Message-ID: <20140401093243.26377.3285@app08.ash-private.bitbucket.org> New issue 495: Docs search: setup.py should return a result https://bitbucket.org/hpk42/pytest/issue/495/docs-search-setuppy-should-return-a-result Thomas G?ttler: There are doc pages containting "setup.py", but the search returns no result: https://pytest.org/latest/search.html?q=setup.py&check_keywords=yes&area=default From commits-noreply at bitbucket.org Wed Apr 2 09:17:50 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:17:50 -0000 Subject: [Pytest-commit] commit/pytest: 22 new changesets Message-ID: <20140402071750.21982.59222@app01.ash-private.bitbucket.org> 22 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/595633b5b497/ Changeset: 595633b5b497 User: hpk42 Date: 2014-03-14 12:49:34 Summary: remove unneccessary indirections and options Affected #: 1 file diff -r 52e080dd60eda8dcf4732baebebebb79160d14ff -r 595633b5b497a24d4c06896290ff4a0783ada19a _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -120,9 +120,6 @@ f.close() return newf - def _makestringio(self): - return TextIO() - def _getcapture(self, method): if method == "fd": return StdCaptureFD( @@ -130,10 +127,7 @@ err=self._maketempfile(), ) elif method == "sys": - return StdCapture( - out=self._makestringio(), - err=self._makestringio(), - ) + return StdCapture(out=TextIO(), err=TextIO()) elif method == "no": return NoCapture() else: https://bitbucket.org/hpk42/pytest/commits/eef0bd2fa258/ Changeset: eef0bd2fa258 User: hpk42 Date: 2014-03-14 12:49:34 Summary: remove externally setting and dealing with "item.outerr" from capturing in favor of a direct interface for adding reporting sections to items. * * * refactor makereport implementation to avoid recursion with __multicall__ Affected #: 7 files diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -151,8 +151,6 @@ def resumecapture_item(self, item): method = self._getmethod(item.config, item.fspath) - if not hasattr(item, 'outerr'): - item.outerr = ('', '') # we accumulate outerr on the item return self.resumecapture(method) def resumecapture(self, method=None): @@ -174,16 +172,10 @@ self.deactivate_funcargs() if hasattr(self, '_capturing'): method = self._capturing + del self._capturing cap = self._method2capture.get(method) if cap is not None: - outerr = cap.suspend() - del self._capturing - if item: - outerr = (item.outerr[0] + outerr[0], - item.outerr[1] + outerr[1]) - return outerr - if hasattr(item, 'outerr'): - return item.outerr + return cap.suspend() return "", "" def activate_funcargs(self, pyfuncitem): @@ -235,18 +227,14 @@ self.suspendcapture() @pytest.mark.tryfirst - def pytest_runtest_makereport(self, __multicall__, item, call): + def pytest_runtest_makereport(self, item, call): funcarg_outerr = self.deactivate_funcargs() - rep = __multicall__.execute() - outerr = self.suspendcapture(item) + out, err = self.suspendcapture(item) if funcarg_outerr is not None: - outerr = (outerr[0] + funcarg_outerr[0], - outerr[1] + funcarg_outerr[1]) - addouterr(rep, outerr) - if not rep.passed or rep.when == "teardown": - outerr = ('', '') - item.outerr = outerr - return rep + out += funcarg_outerr[0] + err += funcarg_outerr[1] + item.add_report_section(call.when, "out", out) + item.add_report_section(call.when, "err", err) error_capsysfderror = "cannot use capsys and capfd at the same time" diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -108,12 +108,14 @@ )) def _write_captured_output(self, report): - sec = dict(report.sections) - for name in ('out', 'err'): - content = sec.get("Captured std%s" % name) - if content: - tag = getattr(Junit, 'system-'+name) - self.append(tag(bin_xml_escape(content))) + for capname in ('out', 'err'): + allcontent = "" + for name, content in report.get_sections("Captured std%s" % + capname): + allcontent += content + if allcontent: + tag = getattr(Junit, 'system-'+capname) + self.append(tag(bin_xml_escape(allcontent))) def append(self, obj): self.tests[-1].append(obj) diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -233,6 +233,7 @@ # used for storing artificial fixturedefs for direct parametrization self._name2pseudofixturedef = {} + #self.extrainit() @property @@ -469,6 +470,14 @@ """ nextitem = None + def __init__(self, name, parent=None, config=None, session=None): + super(Item, self).__init__(name, parent, config, session) + self._report_sections = [] + + def add_report_section(self, when, key, content): + if content: + self._report_sections.append((when, key, content)) + def reportinfo(self): return self.fspath, None, "" diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -35,8 +35,8 @@ if item is not None: capman = item.config.pluginmanager.getplugin("capturemanager") out, err = capman.suspendcapture() - if hasattr(item, 'outerr'): - item.outerr = (item.outerr[0] + out, item.outerr[1] + err) + #if hasattr(item, 'outerr'): + # item.outerr = (item.outerr[0] + out, item.outerr[1] + err) tw = py.io.TerminalWriter() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -178,6 +178,11 @@ except UnicodeEncodeError: out.line("") + def get_sections(self, prefix): + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + passed = property(lambda x: x.outcome == "passed") failed = property(lambda x: x.outcome == "failed") skipped = property(lambda x: x.outcome == "skipped") @@ -191,6 +196,7 @@ duration = call.stop-call.start keywords = dict([(x,1) for x in item.keywords]) excinfo = call.excinfo + sections = [] if not call.excinfo: outcome = "passed" longrepr = None @@ -209,16 +215,18 @@ else: # exception in setup or teardown longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle) + for rwhen, key, content in item._report_sections: + sections.append(("Captured std%s %s" %(key, rwhen), content)) return TestReport(item.nodeid, item.location, keywords, outcome, longrepr, when, - duration=duration) + sections, duration) class TestReport(BaseReport): """ Basic test report object (also used for setup and teardown calls if they fail). """ - def __init__(self, nodeid, location, - keywords, outcome, longrepr, when, sections=(), duration=0, **extra): + def __init__(self, nodeid, location, keywords, outcome, + longrepr, when, sections=(), duration=0, **extra): #: normalized collection node id self.nodeid = nodeid diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -282,9 +282,9 @@ "====* FAILURES *====", "____*____", "*test_capturing_outerr.py:8: ValueError", - "*--- Captured stdout ---*", + "*--- Captured stdout *call*", "1", - "*--- Captured stderr ---*", + "*--- Captured stderr *call*", "2", ]) diff -r 595633b5b497a24d4c06896290ff4a0783ada19a -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -478,10 +478,12 @@ path = testdir.tmpdir.join("test.xml") log = LogXML(str(path), None) ustr = py.builtin._totext("???!", "utf-8") - class report: + from _pytest.runner import BaseReport + class Report(BaseReport): longrepr = ustr sections = [] nodeid = "something" + report = Report() # hopefully this is not too brittle ... log.pytest_sessionstart() https://bitbucket.org/hpk42/pytest/commits/d3abe2e5e194/ Changeset: d3abe2e5e194 User: hpk42 Date: 2014-03-14 12:49:35 Summary: implement a new hook type: hook wrappers using a "yield" to distinguish between working at the front and at the end of a hook call chain. The idea is to make it easier for a plugin to "wrap" a certain hook call and use context managers, in particular allow a major cleanup of capturing. Affected #: 2 files diff -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 _pytest/core.py --- a/_pytest/core.py +++ b/_pytest/core.py @@ -240,18 +240,22 @@ pass l = [] last = [] + wrappers = [] for plugin in plugins: try: meth = getattr(plugin, attrname) - if hasattr(meth, 'tryfirst'): - last.append(meth) - elif hasattr(meth, 'trylast'): - l.insert(0, meth) - else: - l.append(meth) except AttributeError: continue + if hasattr(meth, 'hookwrapper'): + wrappers.append(meth) + elif hasattr(meth, 'tryfirst'): + last.append(meth) + elif hasattr(meth, 'trylast'): + l.insert(0, meth) + else: + l.append(meth) l.extend(last) + l.extend(wrappers) self._listattrcache[key] = list(l) return l @@ -272,6 +276,14 @@ class MultiCall: """ execute a call into multiple python functions/methods. """ + + class WrongHookWrapper(Exception): + """ a hook wrapper does not behave correctly. """ + def __init__(self, func, message): + Exception.__init__(self, func, message) + self.func = func + self.message = message + def __init__(self, methods, kwargs, firstresult=False): self.methods = list(methods) self.kwargs = kwargs @@ -283,16 +295,39 @@ return "" %(status, self.kwargs) def execute(self): - while self.methods: - method = self.methods.pop() - kwargs = self.getkwargs(method) - res = method(**kwargs) - if res is not None: - self.results.append(res) - if self.firstresult: - return res - if not self.firstresult: - return self.results + next_finalizers = [] + try: + while self.methods: + method = self.methods.pop() + kwargs = self.getkwargs(method) + if hasattr(method, "hookwrapper"): + it = method(**kwargs) + next = getattr(it, "next", None) + if next is None: + next = getattr(it, "__next__", None) + if next is None: + raise self.WrongHookWrapper(method, + "wrapper does not contain a yield") + res = next() + next_finalizers.append((method, next)) + else: + res = method(**kwargs) + if res is not None: + self.results.append(res) + if self.firstresult: + return res + if not self.firstresult: + return self.results + finally: + for method, fin in reversed(next_finalizers): + try: + fin() + except StopIteration: + pass + else: + raise self.WrongHookWrapper(method, + "wrapper contain more than one yield") + def getkwargs(self, method): kwargs = {} diff -r eef0bd2fa2583a8ede9efcf8dbd2e9f96d11f77c -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 testing/test_core.py --- a/testing/test_core.py +++ b/testing/test_core.py @@ -523,6 +523,95 @@ res = MultiCall([m1, m2], {}).execute() assert res == [1] + def test_hookwrapper(self): + l = [] + def m1(): + l.append("m1 init") + yield None + l.append("m1 finish") + m1.hookwrapper = True + + def m2(): + l.append("m2") + return 2 + res = MultiCall([m2, m1], {}).execute() + assert res == [2] + assert l == ["m1 init", "m2", "m1 finish"] + l[:] = [] + res = MultiCall([m2, m1], {}, firstresult=True).execute() + assert res == 2 + assert l == ["m1 init", "m2", "m1 finish"] + + def test_hookwrapper_order(self): + l = [] + def m1(): + l.append("m1 init") + yield 1 + l.append("m1 finish") + m1.hookwrapper = True + + def m2(): + l.append("m2 init") + yield 2 + l.append("m2 finish") + m2.hookwrapper = True + res = MultiCall([m2, m1], {}).execute() + assert res == [1, 2] + assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"] + + def test_listattr_hookwrapper_ordering(self): + class P1: + @pytest.mark.hookwrapper + def m(self): + return 17 + + class P2: + def m(self): + return 23 + + class P3: + @pytest.mark.tryfirst + def m(self): + return 19 + + pluginmanager = PluginManager() + p1 = P1() + p2 = P2() + p3 = P3() + pluginmanager.register(p1) + pluginmanager.register(p2) + pluginmanager.register(p3) + methods = pluginmanager.listattr('m') + assert methods == [p2.m, p3.m, p1.m] + ## listattr keeps a cache and deleting + ## a function attribute requires clearing it + #pluginmanager._listattrcache.clear() + #del P1.m.__dict__['tryfirst'] + + def test_hookwrapper_not_yield(self): + def m1(): + pass + m1.hookwrapper = True + + mc = MultiCall([m1], {}) + with pytest.raises(mc.WrongHookWrapper) as ex: + mc.execute() + assert ex.value.func == m1 + assert ex.value.message + + def test_hookwrapper_too_many_yield(self): + def m1(): + yield 1 + yield 2 + m1.hookwrapper = True + + mc = MultiCall([m1], {}) + with pytest.raises(mc.WrongHookWrapper) as ex: + mc.execute() + assert ex.value.func == m1 + assert ex.value.message + + class TestHookRelay: def test_happypath(self): pm = PluginManager() https://bitbucket.org/hpk42/pytest/commits/cfac82eca2e8/ Changeset: cfac82eca2e8 User: hpk42 Date: 2014-03-14 12:49:36 Summary: - turn on capturing before early conftest loading and make terminal writer use the original stream. - avoid resetting capture FDs/sys.stdout for each test by keeping capturing always turned on and looking at snapshotted capturing data during runtest and collection phases. Affected #: 11 files diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -1,12 +1,11 @@ """ - per-test stdout/stderr capturing mechanisms, - ``capsys`` and ``capfd`` function arguments. +per-test stdout/stderr capturing mechanism. + """ -# note: py.io capture was where copied from -# pylib 1.4.20.dev2 (rev 13d9af95547e) import sys import os import tempfile +import contextlib import py import pytest @@ -58,8 +57,18 @@ method = "fd" if method == "fd" and not hasattr(os, "dup"): method = "sys" + pluginmanager = early_config.pluginmanager + if method != "no": + try: + sys.stdout.fileno() + except Exception: + dupped_stdout = sys.stdout + else: + dupped_stdout = dupfile(sys.stdout, buffering=1) + pluginmanager.register(dupped_stdout, "dupped_stdout") + #pluginmanager.add_shutdown(dupped_stdout.close) capman = CaptureManager(method) - early_config.pluginmanager.register(capman, "capturemanager") + pluginmanager.register(capman, "capturemanager") # make sure that capturemanager is properly reset at final shutdown def teardown(): @@ -68,13 +77,13 @@ except ValueError: pass - early_config.pluginmanager.add_shutdown(teardown) + pluginmanager.add_shutdown(teardown) # make sure logging does not raise exceptions at the end def silence_logging_at_shutdown(): if "logging" in sys.modules: sys.modules["logging"].raiseExceptions = False - early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown) + pluginmanager.add_shutdown(silence_logging_at_shutdown) # finally trigger conftest loading but while capturing (issue93) capman.resumecapture() @@ -89,23 +98,20 @@ raise -def addouterr(rep, outerr): - for secname, content in zip(["out", "err"], outerr): - if content: - rep.sections.append(("Captured std%s" % secname, content)) - - class NoCapture: - def startall(self): + def start_capturing(self): pass - def resume(self): + def stop_capturing(self): + pass + + def pop_outerr_to_orig(self): pass def reset(self): pass - def suspend(self): + def readouterr(self): return "", "" @@ -147,7 +153,9 @@ def reset_capturings(self): for cap in self._method2capture.values(): + cap.pop_outerr_to_orig() cap.reset() + self._method2capture.clear() def resumecapture_item(self, item): method = self._getmethod(item.config, item.fspath) @@ -164,9 +172,9 @@ self._capturing = method if cap is None: self._method2capture[method] = cap = self._getcapture(method) - cap.startall() + cap.start_capturing() else: - cap.resume() + cap.pop_outerr_to_orig() def suspendcapture(self, item=None): self.deactivate_funcargs() @@ -175,7 +183,7 @@ del self._capturing cap = self._method2capture.get(method) if cap is not None: - return cap.suspend() + return cap.readouterr() return "", "" def activate_funcargs(self, pyfuncitem): @@ -194,47 +202,68 @@ del self._capturing_funcarg return outerr + @pytest.mark.hookwrapper def pytest_make_collect_report(self, __multicall__, collector): method = self._getmethod(collector.config, collector.fspath) try: self.resumecapture(method) except ValueError: + yield # recursive collect, XXX refactor capturing # to allow for more lightweight recursive capturing return - try: - rep = __multicall__.execute() - finally: - outerr = self.suspendcapture() - addouterr(rep, outerr) - return rep + yield + out, err = self.suspendcapture() + # XXX getting the report from the ongoing hook call is a bit + # of a hack. We need to think about capturing during collection + # and find out if it's really needed fine-grained (per + # collector). + if __multicall__.results: + rep = __multicall__.results[0] + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + + @pytest.mark.hookwrapper + def pytest_runtest_setup(self, item): + with self.item_capture_wrapper(item, "setup"): + yield + + @pytest.mark.hookwrapper + def pytest_runtest_call(self, item): + with self.item_capture_wrapper(item, "call"): + yield + + @pytest.mark.hookwrapper + def pytest_runtest_teardown(self, item): + with self.item_capture_wrapper(item, "teardown"): + yield @pytest.mark.tryfirst - def pytest_runtest_setup(self, item): - self.resumecapture_item(item) + def pytest_keyboard_interrupt(self, excinfo): + self.reset_capturings() @pytest.mark.tryfirst - def pytest_runtest_call(self, item): + def pytest_internalerror(self, excinfo): + self.reset_capturings() + + @contextlib.contextmanager + def item_capture_wrapper(self, item, when): self.resumecapture_item(item) - self.activate_funcargs(item) - - @pytest.mark.tryfirst - def pytest_runtest_teardown(self, item): - self.resumecapture_item(item) - - def pytest_keyboard_interrupt(self, excinfo): - if hasattr(self, '_capturing'): - self.suspendcapture() - - @pytest.mark.tryfirst - def pytest_runtest_makereport(self, item, call): - funcarg_outerr = self.deactivate_funcargs() + if when == "call": + self.activate_funcargs(item) + yield + funcarg_outerr = self.deactivate_funcargs() + else: + yield + funcarg_outerr = None out, err = self.suspendcapture(item) if funcarg_outerr is not None: out += funcarg_outerr[0] err += funcarg_outerr[1] - item.add_report_section(call.when, "out", out) - item.add_report_section(call.when, "err", err) + item.add_report_section(when, "out", out) + item.add_report_section(when, "err", err) error_capsysfderror = "cannot use capsys and capfd at the same time" @@ -263,10 +292,10 @@ class CaptureFixture: def __init__(self, captureclass): - self._capture = captureclass() + self._capture = captureclass(in_=False) def _start(self): - self._capture.startall() + self._capture.start_capturing() def _finalize(self): if hasattr(self, '_capture'): @@ -295,6 +324,8 @@ """ self.targetfd = targetfd if tmpfile is None and targetfd != 0: + # this code path is covered in the tests + # but not used by a regular pytest run f = tempfile.TemporaryFile('wb+') tmpfile = dupfile(f, encoding="UTF-8") f.close() @@ -390,13 +421,13 @@ return getattr(self._stream, name) -class Capture(object): +class StdCaptureBase(object): def reset(self): """ reset sys.stdout/stderr and return captured output as strings. """ if hasattr(self, '_reset'): raise ValueError("was already reset") self._reset = True - outfile, errfile = self.done(save=False) + outfile, errfile = self.stop_capturing(save=False) out, err = "", "" if outfile and not outfile.closed: out = outfile.read() @@ -406,14 +437,16 @@ errfile.close() return out, err - def suspend(self): - """ return current snapshot captures, memorize tempfiles. """ - outerr = self.readouterr() - outfile, errfile = self.done() - return outerr + def pop_outerr_to_orig(self): + """ pop current snapshot out/err capture and flush to orig streams. """ + out, err = self.readouterr() + if out: + self.out.writeorg(out) + if err: + self.err.writeorg(err) -class StdCaptureFD(Capture): +class StdCaptureFD(StdCaptureBase): """ This class allows to capture writes to FD1 and FD2 and may connect a NULL file to FD0 (and prevent reads from sys.stdin). If any of the 0,1,2 file descriptors @@ -464,7 +497,7 @@ except OSError: pass - def startall(self): + def start_capturing(self): if hasattr(self, 'in_'): self.in_.start() if hasattr(self, 'out'): @@ -472,11 +505,10 @@ if hasattr(self, 'err'): self.err.start() - def resume(self): - """ resume capturing with original temp files. """ - self.startall() + #def pytest_sessionfinish(self): + # self.reset_capturings() - def done(self, save=True): + def stop_capturing(self, save=True): """ return (outfile, errfile) and stop capturing. """ outfile = errfile = None if hasattr(self, 'out') and not self.out.tmpfile.closed: @@ -491,16 +523,15 @@ def readouterr(self): """ return snapshot value of stdout/stderr capturings. """ - out = self._readsnapshot('out') - err = self._readsnapshot('err') - return out, err + return self._readsnapshot('out'), self._readsnapshot('err') def _readsnapshot(self, name): - if hasattr(self, name): + try: f = getattr(self, name).tmpfile - else: + except AttributeError: return '' - + if f.tell() == 0: + return '' f.seek(0) res = f.read() enc = getattr(f, "encoding", None) @@ -510,8 +541,17 @@ f.seek(0) return res +class TextCapture(TextIO): + def __init__(self, oldout): + super(TextCapture, self).__init__() + self._oldout = oldout -class StdCapture(Capture): + def writeorg(self, data): + self._oldout.write(data) + self._oldout.flush() + + +class StdCapture(StdCaptureBase): """ This class allows to capture writes to sys.stdout|stderr "in-memory" and will raise errors on tries to read from sys.stdin. It only modifies sys.stdout|stderr|stdin attributes and does not @@ -522,15 +562,15 @@ self._olderr = sys.stderr self._oldin = sys.stdin if out and not hasattr(out, 'file'): - out = TextIO() + out = TextCapture(self._oldout) self.out = out if err: if not hasattr(err, 'write'): - err = TextIO() + err = TextCapture(self._olderr) self.err = err self.in_ = in_ - def startall(self): + def start_capturing(self): if self.out: sys.stdout = self.out if self.err: @@ -538,7 +578,7 @@ if self.in_: sys.stdin = self.in_ = DontReadFromInput() - def done(self, save=True): + def stop_capturing(self, save=True): """ return (outfile, errfile) and stop capturing. """ outfile = errfile = None if self.out and not self.out.closed: @@ -553,9 +593,6 @@ sys.stdin = self._oldin return outfile, errfile - def resume(self): - """ resume capturing with original temp files. """ - self.startall() def readouterr(self): """ return snapshot value of stdout/stderr capturings. """ diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -56,11 +56,15 @@ raise ValueError("not a string or argument list: %r" % (args,)) args = py.std.shlex.split(args) pluginmanager = get_plugin_manager() - if plugins: - for plugin in plugins: - pluginmanager.register(plugin) - return pluginmanager.hook.pytest_cmdline_parse( - pluginmanager=pluginmanager, args=args) + try: + if plugins: + for plugin in plugins: + pluginmanager.register(plugin) + return pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args) + except Exception: + pluginmanager.ensure_shutdown() + raise class PytestPluginManager(PluginManager): def __init__(self, hookspecs=[hookspec]): @@ -612,6 +616,9 @@ self.hook.pytest_logwarning(code=code, message=message, fslocation=None, nodeid=None) + def get_terminal_writer(self): + return self.pluginmanager.getplugin("terminalreporter")._tw + def pytest_cmdline_parse(self, pluginmanager, args): assert self == pluginmanager.config, (self, pluginmanager.config) self.parse(args) diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/genscript.py --- a/_pytest/genscript.py +++ b/_pytest/genscript.py @@ -60,6 +60,7 @@ def pytest_cmdline_main(config): genscript = config.getvalue("genscript") if genscript: + #tw = config.get_terminal_writer() tw = py.io.TerminalWriter() deps = ['py', '_pytest', 'pytest'] if sys.version_info < (2,7): diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -47,6 +47,8 @@ def pytest_cmdline_main(config): if config.option.version: + capman = config.pluginmanager.getplugin("capturemanager") + capman.reset_capturings() p = py.path.local(pytest.__file__) sys.stderr.write("This is pytest version %s, imported from %s\n" % (pytest.__version__, p)) @@ -62,7 +64,7 @@ return 0 def showhelp(config): - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() tw.write(config._parser.optparser.format_help()) tw.line() tw.line() diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -40,7 +40,7 @@ def pytest_cmdline_main(config): if config.option.markers: config.do_configure() - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() for line in config.getini("markers"): name, rest = line.split(":", 1) tw.write("@pytest.mark.%s:" % name, bold=True) diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -34,10 +34,9 @@ if item is not None: capman = item.config.pluginmanager.getplugin("capturemanager") - out, err = capman.suspendcapture() - #if hasattr(item, 'outerr'): - # item.outerr = (item.outerr[0] + out, item.outerr[1] + err) - tw = py.io.TerminalWriter() + if capman: + capman.reset_capturings() + tw = item.config.get_terminal_writer() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") py.std.pdb.Pdb().set_trace(frame) @@ -46,19 +45,20 @@ pytestPDB.item = item pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem - at pytest.mark.tryfirst -def pytest_make_collect_report(__multicall__, collector): - try: - pytestPDB.collector = collector - return __multicall__.execute() - finally: - pytestPDB.collector = None + at pytest.mark.hookwrapper +def pytest_make_collect_report(collector): + pytestPDB.collector = collector + yield + pytestPDB.collector = None def pytest_runtest_makereport(): pytestPDB.item = None class PdbInvoke: def pytest_exception_interact(self, node, call, report): + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.reset_capturings() return _enter_pdb(node, call.excinfo, report) def pytest_internalerror(self, excrepr, excinfo): diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -885,7 +885,7 @@ nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) nodeid.replace(session.fspath.sep, "/") - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() verbose = config.getvalue("verbose") fm = session._fixturemanager diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -135,14 +135,13 @@ self.when = when self.start = time() try: - try: - self.result = func() - except KeyboardInterrupt: - raise - except: - self.excinfo = py.code.ExceptionInfo() - finally: + self.result = func() + except KeyboardInterrupt: self.stop = time() + raise + except: + self.excinfo = py.code.ExceptionInfo() + self.stop = time() def __repr__(self): if self.excinfo: @@ -292,7 +291,8 @@ class CollectReport(BaseReport): - def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra): + def __init__(self, nodeid, outcome, longrepr, result, + sections=(), **extra): self.nodeid = nodeid self.outcome = outcome self.longrepr = longrepr diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -36,7 +36,10 @@ def pytest_configure(config): config.option.verbose -= config.option.quiet - reporter = TerminalReporter(config, sys.stdout) + out = config.pluginmanager.getplugin("dupped_stdout") + #if out is None: + # out = sys.stdout + reporter = TerminalReporter(config, out) config.pluginmanager.register(reporter, 'terminalreporter') if config.option.debug or config.option.traceconfig: def mywriter(tags, args): @@ -44,6 +47,11 @@ reporter.write_line("[traceconfig] " + msg) config.trace.root.setprocessor("pytest:config", mywriter) +def get_terminal_writer(config): + tr = config.pluginmanager.getplugin("terminalreporter") + return tr._tw + + def getreportopt(config): reportopts = "" optvalue = config.option.report diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 bench/bench.py --- a/bench/bench.py +++ b/bench/bench.py @@ -4,8 +4,8 @@ import cProfile import pytest import pstats - script = sys.argv[1] if len(sys.argv) > 1 else "empty.py" - stats = cProfile.run('pytest.cmdline.main([%r])' % script, 'prof') + script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py" + stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof') p = pstats.Stats("prof") p.strip_dirs() p.sort_stats('cumulative') diff -r d3abe2e5e19434f0a08d23b20997e7f31f3f88d1 -r cfac82eca2e810f0c6bf74945981f57e66d1d316 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -736,14 +736,14 @@ class TestStdCapture: def getcapture(self, **kw): cap = capture.StdCapture(**kw) - cap.startall() + cap.start_capturing() return cap def test_capturing_done_simple(self): cap = self.getcapture() sys.stdout.write("hello") sys.stderr.write("world") - outfile, errfile = cap.done() + outfile, errfile = cap.stop_capturing() s = outfile.read() assert s == "hello" s = errfile.read() @@ -787,6 +787,7 @@ print('\xa6') out, err = cap.readouterr() assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') + cap.reset() def test_reset_twice_error(self): cap = self.getcapture() @@ -859,12 +860,13 @@ try: print ("hello") sys.stderr.write("error\n") - out, err = cap.suspend() + out, err = cap.readouterr() + cap.stop_capturing() assert out == "hello\n" assert not err print ("in between") sys.stderr.write("in between\n") - cap.resume() + cap.start_capturing() print ("after") sys.stderr.write("error_after\n") finally: @@ -878,7 +880,7 @@ def getcapture(self, **kw): cap = capture.StdCaptureFD(**kw) - cap.startall() + cap.start_capturing() return cap def test_intermingling(self): @@ -908,7 +910,7 @@ try: os.write(1, "hello".encode("ascii")) os.write(2, "world".encode("ascii")) - outf, errf = capfd.done() + outf, errf = capfd.stop_capturing() finally: capfd.reset() assert outf == tmpfile @@ -924,15 +926,15 @@ def test_stdout(): os.close(1) cap = StdCaptureFD(out=True, err=False, in_=False) - cap.done() + cap.stop_capturing() def test_stderr(): os.close(2) cap = StdCaptureFD(out=False, err=True, in_=False) - cap.done() + cap.stop_capturing() def test_stdin(): os.close(0) cap = StdCaptureFD(out=False, err=False, in_=True) - cap.done() + cap.stop_capturing() """) result = testdir.runpytest("--capture=fd") assert result.ret == 0 @@ -941,8 +943,8 @@ def test_capture_not_started_but_reset(): capsys = capture.StdCapture() - capsys.done() - capsys.done() + capsys.stop_capturing() + capsys.stop_capturing() capsys.reset() @@ -951,7 +953,7 @@ capsys = capture.StdCapture() try: cap = capture.StdCaptureFD(patchsys=False) - cap.startall() + cap.start_capturing() sys.stdout.write("hello") sys.stderr.write("world") oswritebytes(1, "1") @@ -970,10 +972,9 @@ tmpfile = True cap = capture.StdCaptureFD(out=False, err=tmpfile) try: - cap.startall() + cap.start_capturing() capfile = cap.err.tmpfile - cap.suspend() - cap.resume() + cap.readouterr() finally: cap.reset() capfile2 = cap.err.tmpfile @@ -990,22 +991,25 @@ import py, logging from _pytest import capture cap = capture.%s(out=False, in_=False) - cap.startall() + cap.start_capturing() logging.warn("hello1") - outerr = cap.suspend() + outerr = cap.readouterr() print ("suspend, captured %%s" %%(outerr,)) logging.warn("hello2") - cap.resume() + cap.pop_outerr_to_orig() logging.warn("hello3") - outerr = cap.suspend() + outerr = cap.readouterr() print ("suspend2, captured %%s" %% (outerr,)) """ % (method,)) result = testdir.runpython(p) - result.stdout.fnmatch_lines([ - "suspend, captured*hello1*", - "suspend2, captured*hello2*WARNING:root:hello3*", - ]) + result.stdout.fnmatch_lines(""" + suspend, captured*hello1* + suspend2, captured*WARNING:root:hello3* + """) + result.stderr.fnmatch_lines(""" + WARNING:root:hello2 + """) assert "atexit" not in result.stderr.str() https://bitbucket.org/hpk42/pytest/commits/d774e7c11ee6/ Changeset: d774e7c11ee6 User: hpk42 Date: 2014-03-14 12:49:37 Summary: simplify pdb disabling of capturing, also accomodate the new semantics that capturing is always on during a test session. Affected #: 1 file diff -r cfac82eca2e810f0c6bf74945981f57e66d1d316 -r d774e7c11ee60f641f360b366d2f4d8165ec8ed1 _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -16,43 +16,30 @@ if config.getvalue("usepdb"): config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') - old_trace = py.std.pdb.set_trace + old = (py.std.pdb.set_trace, pytestPDB._pluginmanager) def fin(): - py.std.pdb.set_trace = old_trace + py.std.pdb.set_trace, pytestPDB._pluginmanager = old py.std.pdb.set_trace = pytest.set_trace + pytestPDB._pluginmanager = config.pluginmanager config._cleanup.append(fin) class pytestPDB: """ Pseudo PDB that defers to the real pdb. """ - item = None - collector = None + _pluginmanager = None def set_trace(self): """ invoke PDB set_trace debugging, dropping any IO capturing. """ frame = sys._getframe().f_back - item = self.item or self.collector - - if item is not None: - capman = item.config.pluginmanager.getplugin("capturemanager") + capman = None + if self._pluginmanager is not None: + capman = self._pluginmanager.getplugin("capturemanager") if capman: capman.reset_capturings() - tw = item.config.get_terminal_writer() + tw = py.io.TerminalWriter() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") py.std.pdb.Pdb().set_trace(frame) -def pdbitem(item): - pytestPDB.item = item -pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem - - at pytest.mark.hookwrapper -def pytest_make_collect_report(collector): - pytestPDB.collector = collector - yield - pytestPDB.collector = None - -def pytest_runtest_makereport(): - pytestPDB.item = None class PdbInvoke: def pytest_exception_interact(self, node, call, report): https://bitbucket.org/hpk42/pytest/commits/25c6de59b09b/ Changeset: 25c6de59b09b User: hpk42 Date: 2014-03-14 15:58:16 Summary: tentatively fix py33 and py25 compat Affected #: 1 file diff -r d774e7c11ee60f641f360b366d2f4d8165ec8ed1 -r 25c6de59b09b45455976fa3139cdff360d95b82c _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -2,6 +2,8 @@ per-test stdout/stderr capturing mechanism. """ +from __future__ import with_statement + import sys import os import tempfile @@ -366,12 +368,9 @@ def writeorg(self, data): """ write a string to the original file descriptor """ - tempfp = tempfile.TemporaryFile() - try: - os.dup2(self._savefd, tempfp.fileno()) - tempfp.write(data) - finally: - tempfp.close() + if py.builtin._istext(data): + data = data.encode("utf8") # XXX use encoding of original stream + os.write(self._savefd, data) def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): https://bitbucket.org/hpk42/pytest/commits/b36800a30a51/ Changeset: b36800a30a51 User: hpk42 Date: 2014-03-27 13:57:54 Summary: merge in current default Affected #: 22 files diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 .hgignore --- a/.hgignore +++ b/.hgignore @@ -9,6 +9,7 @@ bin/ include/ .Python/ +.env/ # These lines are suggested according to the svn:ignore property # Feel free to enable them by uncommenting them @@ -27,6 +28,7 @@ *.egg-info issue/ env/ +env3/ 3rdparty/ .tox .cache diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -38,3 +38,5 @@ Mark Abramowitz Piotr Banaszkiewicz Jurko Gospodneti? +Marc Schlaich +Christopher Gilling diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,19 @@ - change skips into warnings for test classes with an __init__ and callables in test modules which look like a test but are not functions. +- fix issue #479: properly handle nose/unittest(2) SkipTest exceptions + during collection/loading of test modules. Thanks to Marc Schlaich + for the complete PR. + +- fix issue490: include pytest_load_initial_conftests in documentation + and improve docstring. + +- fix issue472: clarify that ``pytest.config.getvalue()`` cannot work + if it's triggered ahead of command line parsing. + +- merge PR123: improved integration with mock.patch decorator on tests. + + 2.5.2 ----------------------------------- diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -440,7 +440,7 @@ if len(option) == 2 or option[2] == ' ': return_list.append(option) if option[2:] == short_long.get(option.replace('-', '')): - return_list.append(option) + return_list.append(option.replace(' ', '=')) action._formatted_action_invocation = ', '.join(return_list) return action._formatted_action_invocation diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/hookspec.py --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -53,8 +53,8 @@ pytest_cmdline_main.firstresult = True def pytest_load_initial_conftests(args, early_config, parser): - """ implements loading initial conftests. - """ + """ implements the loading of initial conftest files ahead + of command line option parsing. """ def pytest_configure(config): """ called after command line options have been parsed diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -8,7 +8,7 @@ except ImportError: from UserDict import DictMixin as MappingMixin -from _pytest.runner import collect_one_node, Skipped +from _pytest.runner import collect_one_node tracebackcutdir = py.path.local(_pytest.__file__).dirpath() @@ -409,10 +409,6 @@ and thus iteratively build a tree. """ - # the set of exceptions to interpret as "Skip the whole module" during - # collection - skip_exceptions = (Skipped,) - class CollectError(Exception): """ an error during collection, contains a custom message. """ diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/nose.py --- a/_pytest/nose.py +++ b/_pytest/nose.py @@ -1,17 +1,27 @@ """ run test suites written for nose. """ -import pytest, py import sys + +import py +import pytest from _pytest import unittest + +def get_skip_exceptions(): + skip_classes = set() + for module_name in ('unittest', 'unittest2', 'nose'): + mod = sys.modules.get(module_name) + if hasattr(mod, 'SkipTest'): + skip_classes.add(mod.SkipTest) + return tuple(skip_classes) + + def pytest_runtest_makereport(__multicall__, item, call): - SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None) - if SkipTest: - if call.excinfo and call.excinfo.errisinstance(SkipTest): - # let's substitute the excinfo with a pytest.skip one - call2 = call.__class__(lambda: - pytest.skip(str(call.excinfo.value)), call.when) - call.excinfo = call2.excinfo + if call.excinfo and call.excinfo.errisinstance(get_skip_exceptions()): + # let's substitute the excinfo with a pytest.skip one + call2 = call.__class__(lambda: + pytest.skip(str(call.excinfo.value)), call.when) + call.excinfo = call2.excinfo @pytest.mark.trylast @@ -38,13 +48,8 @@ # #call_optional(item._nosegensetup, 'teardown') # del item.parent._nosegensetup + def pytest_make_collect_report(collector): - SkipTest = getattr(sys.modules.get('unittest', None), 'SkipTest', None) - if SkipTest is not None: - collector.skip_exceptions += (SkipTest,) - SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None) - if SkipTest is not None: - collector.skip_exceptions += (SkipTest,) if isinstance(collector, pytest.Generator): call_optional(collector.obj, 'setup') diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1841,7 +1841,13 @@ if startindex is None: startindex = inspect.ismethod(function) and 1 or 0 if realfunction != function: - startindex += len(getattr(function, "patchings", [])) + mock = sys.modules.get('mock') + if mock is not None: + for patching in getattr(function, "patchings", []): + if not patching.attribute_name and patching.new is mock.DEFAULT: + startindex += 1 + else: + startindex += len(getattr(function, "patchings", [])) function = realfunction argnames = inspect.getargs(py.code.getrawcode(function))[0] defaults = getattr(function, 'func_defaults', diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -274,7 +274,9 @@ if not call.excinfo: outcome = "passed" else: - if call.excinfo.errisinstance(collector.skip_exceptions): + from _pytest import nose + skip_exceptions = (Skipped,) + nose.get_skip_exceptions() + if call.excinfo.errisinstance(skip_exceptions): outcome = "skipped" r = collector._repr_failure_py(call.excinfo, "line").reprcrash longrepr = (str(r.path), r.lineno, r.message) diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/_templates/links.html --- a/doc/en/_templates/links.html +++ b/doc/en/_templates/links.html @@ -4,7 +4,7 @@
  • Contribution Guide
  • pytest @ PyPI
  • pytest @ Bitbucket
  • -
  • 3rd party plugins (beta)
  • +
  • 3rd party plugins
  • Issue Tracker
  • PDF Documentation diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/goodpractises.txt --- a/doc/en/goodpractises.txt +++ b/doc/en/goodpractises.txt @@ -1,4 +1,3 @@ - .. highlightlang:: python .. _`goodpractises`: @@ -69,7 +68,7 @@ - **avoid "__init__.py" files in your test directories**. This way your tests can run easily against an installed version - of ``mypkg``, independently from if the installed package contains + of ``mypkg``, independently from the installed package if it contains the tests or not. - With inlined tests you might put ``__init__.py`` into test @@ -190,12 +189,16 @@ user_options = [] def initialize_options(self): pass + def finalize_options(self): pass + def run(self): import sys,subprocess errno = subprocess.call([sys.executable, 'runtests.py']) raise SystemExit(errno) + + setup( #..., cmdclass = {'test': PyTest}, @@ -220,20 +223,24 @@ Most often it is better to use tox_ instead, but here is how you can get started with setuptools integration:: + import sys + from setuptools.command.test import test as TestCommand - import sys + class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True + def run_tests(self): #import here, cause outside the eggs aren't loaded import pytest errno = pytest.main(self.test_args) sys.exit(errno) + setup( #..., tests_require=['pytest'], diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -64,9 +64,10 @@ pip uninstall pytest-NAME If a plugin is installed, ``pytest`` automatically finds and integrates it, -there is no need to activate it. We have a :doc:`beta page listing -all 3rd party plugins and their status ` and here -is a little annotated list for some popular plugins: +there is no need to activate it. We have a :doc:`page listing +all 3rd party plugins and their status against the latest py.test version +` and here is a little annotated list +for some popular plugins: .. _`django`: https://www.djangoproject.com/ @@ -109,7 +110,11 @@ * `oejskit `_: a plugin to run javascript unittests in life browsers -You may discover more plugins through a `pytest- pypi.python.org search`_. +To see a complete list of all plugins with their latest testing +status against different py.test and Python versions, please visit +`pytest-plugs `_. + +You may also discover more plugins through a `pytest- pypi.python.org search`_. .. _`available installable plugins`: .. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search @@ -304,6 +309,7 @@ .. currentmodule:: _pytest.hookspec +.. autofunction:: pytest_load_initial_conftests .. autofunction:: pytest_cmdline_preparse .. autofunction:: pytest_cmdline_parse .. autofunction:: pytest_namespace @@ -315,7 +321,7 @@ Generic "runtest" hooks ------------------------------ -All all runtest related hooks receive a :py:class:`pytest.Item` object. +All runtest related hooks receive a :py:class:`pytest.Item` object. .. autofunction:: pytest_runtest_protocol .. autofunction:: pytest_runtest_setup diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/bitbucket.png Binary file doc/en/plugins_index/bitbucket.png has changed diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/github.png Binary file doc/en/plugins_index/github.png has changed diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/index.txt --- a/doc/en/plugins_index/index.txt +++ b/doc/en/plugins_index/index.txt @@ -3,114 +3,125 @@ List of Third-Party Plugins =========================== -================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= ============================================================================================================================================= - Name Py27 Py33 Repository Summary -================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= ============================================================================================================================================= - `pytest-bdd `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2 https://github.com/olegpidsadnyi/pytest-bdd BDD for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2 - `pytest-bdd-splinter `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 https://github.com/olegpidsadnyi/pytest-bdd-splinter Splinter subplugin for Pytest BDD plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 - `pytest-bench `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2 http://github.com/concordusapps/pytest-bench Benchmark utility that plugs into pytest. - :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2 - `pytest-blockage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2 https://github.com/rob-b/pytest-blockage Disable network requests during a test run. - :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2 - `pytest-browsermob-proxy `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 https://github.com/davehunt/pytest-browsermob-proxy BrowserMob proxy plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 - `pytest-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2 http://github.com/nibrahim/pytest_bugzilla py.test bugzilla integration plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2 - `pytest-cache `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hpk42/pytest-cache/ pytest plugin with mechanisms for caching across test runs - :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2 - `pytest-capturelog `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2 http://bitbucket.org/memedough/pytest-capturelog/overview py.test plugin to capture log messages - :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2 - `pytest-codecheckers `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2 http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ pytest plugin to add source code sanity checks (pep8 and friends) - :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2 - `pytest-contextfixture `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2 http://github.com/pelme/pytest-contextfixture/ Define pytest fixtures as context managers. - :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2 - `pytest-couchdbkit `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit py.test extension for per-test couchdb databases using couchdbkit - :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 - `pytest-cov `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2 http://bitbucket.org/memedough/pytest-cov/overview py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing - :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2 - `pytest-dbfixtures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 https://github.com/clearcode/pytest-dbfixtures dbfixtures plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 - `pytest-django `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2 http://pytest-django.readthedocs.org/ A Django plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2 - `pytest-django-lite `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2 https://github.com/dcramer/pytest-django-lite The bare minimum to integrate py.test with Django. - :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2 - `pytest-figleaf `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hpk42/pytest-figleaf py.test figleaf coverage plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2 - `pytest-flakes `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2 https://github.com/fschulze/pytest-flakes pytest plugin to check source code with pyflakes - :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2 - `pytest-greendots `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2 UNKNOWN Green progress dots - :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2 - `pytest-growl `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2 UNKNOWN Growl notifications for pytest results. - :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2 - `pytest-httpretty `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2 http://github.com/papaeye/pytest-httpretty A thin wrapper of HTTPretty for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2 - `pytest-incremental `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2 https://bitbucket.org/schettino72/pytest-incremental an incremental test runner (pytest plugin) - :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2 - `pytest-instafail `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2 https://github.com/jpvanhal/pytest-instafail py.test plugin to show failures instantly - :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2 - `pytest-ipdb `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2 https://github.com/mverteuil/pytest-ipdb A py.test plug-in to enable drop to ipdb debugger on test failure. - :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2 - `pytest-jira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2 http://github.com/jlaska/pytest_jira py.test JIRA integration plugin, using markers - :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2 - `pytest-konira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2 http://github.com/alfredodeza/pytest-konira Run Konira DSL tests with py.test - :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2 - `pytest-localserver `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2 http://bitbucket.org/basti/pytest-localserver/ py.test plugin to test server connections locally. - :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2 - `pytest-marker-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 http://github.com/eanxgeek/pytest_marker_bugzilla py.test bugzilla integration plugin, using markers - :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 - `pytest-markfiltration `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2 https://github.com/adamgoucher/pytest-markfiltration UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2 - `pytest-marks `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2 https://github.com/adamgoucher/pytest-marks UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2 - `pytest-monkeyplus `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hsoft/pytest-monkeyplus/ pytest's monkeypatch subclass with extra functionalities - :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 - `pytest-mozwebqa `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 https://github.com/davehunt/pytest-mozwebqa Mozilla WebQA plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 - `pytest-oerp `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2 http://github.com/santagada/pytest-oerp/ pytest plugin to test OpenERP modules - :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2 - `pytest-osxnotify `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2 https://github.com/dbader/pytest-osxnotify OS X notifications for py.test results. - :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2 - `pytest-paste-config `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2 UNKNOWN Allow setting the path to a paste config file - :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2 - `pytest-pep8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hpk42/pytest-pep8/ pytest plugin to check PEP8 requirements - :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2 - `pytest-poo `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2 http://github.com/pelme/pytest-poo Visualize your crappy tests - :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2 - `pytest-pydev `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2 http://bitbucket.org/basti/pytest-pydev/ py.test plugin to connect to a remote debug server with PyDev or PyCharm. - :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2 - `pytest-pythonpath `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2 https://github.com/bigsassy/pytest-pythonpath pytest plugin for adding to the PYTHONPATH from command line or configs. - :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2 - `pytest-qt `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2 http://github.com/nicoddemus/pytest-qt pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications. - :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2 - `pytest-quickcheck `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2 http://bitbucket.org/t2y/pytest-quickcheck/ pytest plugin to generate random data inspired by QuickCheck - :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2 - `pytest-rage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2 http://github.com/santagada/pytest-rage/ pytest plugin to implement PEP712 - :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2 - `pytest-random `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2 https://github.com/klrmn/pytest-random py.test plugin to randomize tests - :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2 - `pytest-rerunfailures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 https://github.com/klrmn/pytest-rerunfailures py.test plugin to re-run tests to eliminate flakey failures - :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 - `pytest-runfailed `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2 http://github.com/dmerejkowsky/pytest-runfailed implement a --failed option for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 - `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 https://bitbucket.org/jaraco/pytest-runner UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 - `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 http://pivotfinland.com/pytest-sugar/ py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). - :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 - `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 http://bitbucket.org/flub/pytest-timeout/ pytest plugin to abort tests after a timeout - :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 - `pytest-twisted `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 https://github.com/schmir/pytest-twisted A twisted plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2 - `pytest-xdist `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hpk42/pytest-xdist py.test xdist plugin for distributed testing and loop-on-failing modes - :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2 - `pytest-xprocess `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2 http://bitbucket.org/hpk42/pytest-xprocess/ pytest plugin to manage external processes across test runs - :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2 - `pytest-yamlwsgi `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 UNKNOWN Run tests against wsgi apps defined in yaml - :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 - `pytest-zap `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2 https://github.com/davehunt/pytest-zap OWASP ZAP plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2 +The table below contains a listing of plugins found in PyPI and +their status when tested using py.test **2.5.2** and python 2.7 and +3.3. -================================================================================== =========================================================================================================== =========================================================================================================== ============================================================= ============================================================================================================================================= +A complete listing can also be found at +`pytest-plugs `_, which contains tests +status against other py.test releases. -*(Updated on 2014-02-11)* + +==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + Name Py27 Py33 Repo Summary +==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + `pytest-bdd `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2 .. image:: github.png BDD for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd + `pytest-bdd-splinter `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 .. image:: github.png Splinter subplugin for Pytest BDD plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd-splinter + `pytest-bench `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2 .. image:: github.png Benchmark utility that plugs into pytest. + :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2 :target: http://github.com/concordusapps/pytest-bench + `pytest-blockage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2 .. image:: github.png Disable network requests during a test run. + :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2 :target: https://github.com/rob-b/pytest-blockage + `pytest-browsermob-proxy `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 .. image:: github.png BrowserMob proxy plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-browsermob-proxy + `pytest-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/nibrahim/pytest_bugzilla + `pytest-cache `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin with mechanisms for caching across test runs + :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-cache/ + `pytest-capturelog `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to capture log messages + :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-capturelog/overview + `pytest-codecheckers `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to add source code sanity checks (pep8 and friends) + :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ + `pytest-contextfixture `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2 .. image:: github.png Define pytest fixtures as context managers. + :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-contextfixture/ + `pytest-couchdbkit `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test extension for per-test couchdb databases using couchdbkit + :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit + `pytest-cov `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing + :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-cov/overview + `pytest-dbfixtures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 .. image:: github.png dbfixtures plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 :target: https://github.com/clearcode/pytest-dbfixtures + `pytest-dbus-notification `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 .. image:: github.png D-BUS notifications for pytest results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 :target: https://github.com/bmathieu33/pytest-dbus-notification + `pytest-django `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2 `link `_ A Django plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2 + `pytest-django-lite `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2 .. image:: github.png The bare minimum to integrate py.test with Django. + :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2 :target: https://github.com/dcramer/pytest-django-lite + `pytest-figleaf `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test figleaf coverage plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-figleaf + `pytest-flakes `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to check source code with pyflakes + :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2 :target: https://github.com/fschulze/pytest-flakes + `pytest-greendots `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2 ? Green progress dots + :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2 + `pytest-growl `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2 ? Growl notifications for pytest results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2 + `pytest-httpretty `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2 .. image:: github.png A thin wrapper of HTTPretty for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2 :target: http://github.com/papaeye/pytest-httpretty + `pytest-incremental `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png an incremental test runner (pytest plugin) + :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/schettino72/pytest-incremental + `pytest-instafail `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to show failures instantly + :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2 :target: https://github.com/jpvanhal/pytest-instafail + `pytest-ipdb `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2 .. image:: github.png A py.test plug-in to enable drop to ipdb debugger on test failure. + :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2 :target: https://github.com/mverteuil/pytest-ipdb + `pytest-jira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test JIRA integration plugin, using markers + :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2 :target: http://github.com/jlaska/pytest_jira + `pytest-konira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2 .. image:: github.png Run Konira DSL tests with py.test + :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2 :target: http://github.com/alfredodeza/pytest-konira + `pytest-localserver `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to test server connections locally. + :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-localserver/ + `pytest-marker-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin, using markers + :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/eanxgeek/pytest_marker_bugzilla + `pytest-markfiltration `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-markfiltration + `pytest-marks `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-marks + `pytest-monkeyplus `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest's monkeypatch subclass with extra functionalities + :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/ + `pytest-mozwebqa `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 .. image:: github.png Mozilla WebQA plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-mozwebqa + `pytest-oerp `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to test OpenERP modules + :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-oerp/ + `pytest-osxnotify `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2 .. image:: github.png OS X notifications for py.test results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2 :target: https://github.com/dbader/pytest-osxnotify + `pytest-paste-config `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2 ? Allow setting the path to a paste config file + :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2 + `pytest-pep8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to check PEP8 requirements + :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-pep8/ + `pytest-poo `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2 .. image:: github.png Visualize your crappy tests + :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-poo + `pytest-pydev `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to connect to a remote debug server with PyDev or PyCharm. + :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-pydev/ + `pytest-pythonpath `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin for adding to the PYTHONPATH from command line or configs. + :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2 :target: https://github.com/bigsassy/pytest-pythonpath + `pytest-qt `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications. + :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2 :target: http://github.com/nicoddemus/pytest-qt + `pytest-quickcheck `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to generate random data inspired by QuickCheck + :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/t2y/pytest-quickcheck/ + `pytest-rage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to implement PEP712 + :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-rage/ + `pytest-random `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to randomize tests + :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-random + `pytest-rerunfailures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to re-run tests to eliminate flakey failures + :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-rerunfailures + `pytest-runfailed `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2 .. image:: github.png implement a --failed option for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed + `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner + `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 `link `_ py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). + :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 + `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout + :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ + `pytest-twisted `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 .. image:: github.png A twisted plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2 :target: https://github.com/schmir/pytest-twisted + `pytest-xdist `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test xdist plugin for distributed testing and loop-on-failing modes + :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xdist + `pytest-xprocess `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to manage external processes across test runs + :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xprocess/ + `pytest-yamlwsgi `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 ? Run tests against wsgi apps defined in yaml + :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 + `pytest-zap `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2 .. image:: github.png OWASP ZAP plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-zap + +==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + +*(Updated on 2014-02-18)* diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/plugins_index/plugins_index.py --- a/doc/en/plugins_index/plugins_index.py +++ b/doc/en/plugins_index/plugins_index.py @@ -1,6 +1,13 @@ """ Script to generate the file `index.txt` with information about -pytest plugins taken directly from a live PyPI server. +pytest plugins taken directly from PyPI. + +Usage: + python plugins_index.py + +This command will update `index.txt` in the same directory found as this script. +This should be issued before every major documentation release to obtain latest +versions from PyPI. Also includes plugin compatibility between different python and pytest versions, obtained from http://pytest-plugs.herokuapp.com. @@ -66,9 +73,34 @@ :param plugins: list of (name, version) :param client: ServerProxy """ + def get_repo_markup(repo): + """ + obtains appropriate markup for the given repository, as two lines + that should be output in the same table row. We use this to display an icon + for known repository hosts (github, etc), just a "?" char when + repository is not registered in pypi or a simple link otherwise. + """ + target = repo + if 'github.com' in repo: + image = 'github.png' + elif 'bitbucket.org' in repo: + image = 'bitbucket.png' + elif repo.lower() == 'unknown': + return '?', '' + else: + image = None + + if image is not None: + image_markup = '.. image:: %s' % image + target_markup = ' :target: %s' % repo + pad_right = ('%-' + str(len(target_markup)) + 's') + return pad_right % image_markup, target_markup + else: + return '`link <%s>`_' % target, '' + rows = [] ColumnData = namedtuple('ColumnData', 'text link') - headers = ['Name', 'Py27', 'Py33', 'Repository', 'Summary'] + headers = ['Name', 'Py27', 'Py33', 'Repo', 'Summary'] pytest_version = pytest.__version__ repositories = obtain_override_repositories() print('*** pytest-{0} ***'.format(pytest_version)) @@ -83,6 +115,9 @@ name=package_name, version=version) + repository = repositories.get(package_name, release_data['home_page']) + repo_markup_1, repo_markup_2 = get_repo_markup(repository) + # first row: name, images and simple links url = '.. image:: {site}/status/{name}-latest' image_url = url.format(**common_params) @@ -94,7 +129,7 @@ ColumnData(image_url.format(py='py33', pytest=pytest_version), None), ColumnData( - repositories.get(package_name, release_data['home_page']), + repo_markup_1, None), ColumnData(release_data['summary'], None), ) @@ -112,8 +147,9 @@ None), ColumnData(output_url.format(py='py33', pytest=pytest_version), None), + ColumnData(repo_markup_2, None), ColumnData('', None), - ColumnData('', None), + ) assert len(row) == len(headers) rows.append(row) @@ -167,11 +203,9 @@ return ' '.join(char * length for length in column_lengths) with open(filename, 'w') as f: - # write welcome - print('.. _plugins_index:', file=f) - print(file=f) - print('List of Third-Party Plugins', file=f) - print('===========================', file=f) + # header + header_text = HEADER.format(pytest_version=pytest.__version__) + print(header_text, file=f) print(file=f) # table @@ -232,5 +266,21 @@ return 0 +# header for the plugins_index page +HEADER = '''.. _plugins_index: + +List of Third-Party Plugins +=========================== + +The table below contains a listing of plugins found in PyPI and +their status when tested using py.test **{pytest_version}** and python 2.7 and +3.3. + +A complete listing can also be found at +`pytest-plugs `_, which contains tests +status against other py.test releases. +''' + + if __name__ == '__main__': sys.exit(main(sys.argv)) diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -286,4 +286,9 @@ def test_function(...): pass +.. note:: + You cannot use ``pytest.config.getvalue()`` in code + imported before py.test's argument parsing takes place. For example, + ``conftest.py`` files are imported before command line parsing and thus + ``config.getvalue()`` will not execute correctly. diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/python/collect.py --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -277,7 +277,7 @@ assert hasattr(modcol.obj, 'test_func') def test_function_as_object_instance_ignored(self, testdir): - item = testdir.makepyfile(""" + testdir.makepyfile(""" class A: def __call__(self, tmpdir): 0/0 diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -124,12 +124,16 @@ def test_hello(self, abspath): os.path.abspath("hello") abspath.assert_any_call("hello") + def mock_basename(path): + return "mock_basename" @mock.patch("os.path.abspath") @mock.patch("os.path.normpath") + @mock.patch("os.path.basename", new=mock_basename) def test_someting(normpath, abspath, tmpdir): abspath.return_value = "this" os.path.normpath(os.path.abspath("hello")) normpath.assert_any_call("this") + assert os.path.basename("123") == "mock_basename" """) reprec = testdir.inline_run() reprec.assertoutcome(passed=2) diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -533,6 +533,24 @@ assert 'hello19' not in result.stdout.str() + at pytest.mark.xfail(reason='demonstrate #412') +def test_capture_badoutput(testdir): + testdir.makepyfile(""" + import os + + def test_func(): + omg = bytearray([1,129,1]) + os.write(1, omg) + assert 0 + """) + result = testdir.runpytest('--cap=fd') + #this fails on python3 - fnmatch first for debugging + result.stdout.fnmatch_lines([ + '*1 failed*', + ]) + assert result.ret == 1 + + def test_capture_early_option_parsing(testdir): testdir.makeconftest(""" def pytest_runtest_setup(): diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 testing/test_nose.py --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -330,12 +330,26 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1, skipped=1) + def test_SkipTest_during_collection(testdir): - testdir.makepyfile(""" + p = testdir.makepyfile(""" import nose raise nose.SkipTest("during collection") def test_failing(): assert False """) + result = testdir.runpytest(p) + outcome = result.parseoutcomes() + outcome.pop('seconds') + assert outcome == dict(skipped=1) + + +def test_SkipTest_in_test(testdir): + testdir.makepyfile(""" + import nose + + def test_skipping(): + raise nose.SkipTest("in test") + """) reprec = testdir.inline_run() reprec.assertoutcome(skipped=1) diff -r 25c6de59b09b45455976fa3139cdff360d95b82c -r b36800a30a51911876ae23ff0fc187145e0f9f03 tox.ini --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ commands= py.test --lsof -rfsxX --junitxml={envlogdir}/junit-{envname}.xml [] deps= nose + mock [testenv:genscript] changedir=. https://bitbucket.org/hpk42/pytest/commits/b7aa02ac2440/ Changeset: b7aa02ac2440 User: hpk42 Date: 2014-03-28 07:03:34 Summary: remove unused "suspend/resume" on capturing, some formatting cleanup Affected #: 2 files diff -r b36800a30a51911876ae23ff0fc187145e0f9f03 -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -426,7 +426,7 @@ if hasattr(self, '_reset'): raise ValueError("was already reset") self._reset = True - outfile, errfile = self.stop_capturing(save=False) + outfile, errfile = self.stop_capturing() out, err = "", "" if outfile and not outfile.closed: out = outfile.read() @@ -452,24 +452,9 @@ is invalid it will not be captured. """ def __init__(self, out=True, err=True, in_=True, patchsys=True): - self._options = { - "out": out, - "err": err, - "in_": in_, - "patchsys": patchsys, - } - self._save() - - def _save(self): - in_ = self._options['in_'] - out = self._options['out'] - err = self._options['err'] - patchsys = self._options['patchsys'] if in_: try: - self.in_ = FDCapture( - 0, tmpfile=None, - patchsys=patchsys) + self.in_ = FDCapture(0, tmpfile=None, patchsys=patchsys) except OSError: pass if out: @@ -477,10 +462,7 @@ if hasattr(out, 'write'): tmpfile = out try: - self.out = FDCapture( - 1, tmpfile=tmpfile, - patchsys=patchsys) - self._options['out'] = self.out.tmpfile + self.out = FDCapture(1, tmpfile=tmpfile, patchsys=patchsys) except OSError: pass if err: @@ -489,10 +471,7 @@ else: tmpfile = None try: - self.err = FDCapture( - 2, tmpfile=tmpfile, - patchsys=patchsys) - self._options['err'] = self.err.tmpfile + self.err = FDCapture(2, tmpfile=tmpfile, patchsys=patchsys) except OSError: pass @@ -507,7 +486,7 @@ #def pytest_sessionfinish(self): # self.reset_capturings() - def stop_capturing(self, save=True): + def stop_capturing(self): """ return (outfile, errfile) and stop capturing. """ outfile = errfile = None if hasattr(self, 'out') and not self.out.tmpfile.closed: @@ -516,8 +495,6 @@ errfile = self.err.done() if hasattr(self, 'in_'): self.in_.done() - if save: - self._save() return outfile, errfile def readouterr(self): @@ -577,7 +554,7 @@ if self.in_: sys.stdin = self.in_ = DontReadFromInput() - def stop_capturing(self, save=True): + def stop_capturing(self): """ return (outfile, errfile) and stop capturing. """ outfile = errfile = None if self.out and not self.out.closed: diff -r b36800a30a51911876ae23ff0fc187145e0f9f03 -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -873,25 +873,6 @@ pytest.raises(IOError, "sys.stdin.read()") out, err = cap.reset() - def test_suspend_resume(self): - cap = self.getcapture(out=True, err=False, in_=False) - try: - print ("hello") - sys.stderr.write("error\n") - out, err = cap.readouterr() - cap.stop_capturing() - assert out == "hello\n" - assert not err - print ("in between") - sys.stderr.write("in between\n") - cap.start_capturing() - print ("after") - sys.stderr.write("error_after\n") - finally: - out, err = cap.reset() - assert out == "after\n" - assert not err - class TestStdCaptureFD(TestStdCapture): pytestmark = needsosdup @@ -925,12 +906,9 @@ @needsosdup def test_stdcapture_fd_tmpfile(tmpfile): capfd = capture.StdCaptureFD(out=tmpfile) - try: - os.write(1, "hello".encode("ascii")) - os.write(2, "world".encode("ascii")) - outf, errf = capfd.stop_capturing() - finally: - capfd.reset() + os.write(1, "hello".encode("ascii")) + os.write(2, "world".encode("ascii")) + outf, errf = capfd.stop_capturing() assert outf == tmpfile https://bitbucket.org/hpk42/pytest/commits/080d7f3e486f/ Changeset: 080d7f3e486f User: hpk42 Date: 2014-03-28 07:03:37 Summary: unify and normalize Sys/FD Capturing classes * * * more unification Affected #: 2 files diff -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 -r 080d7f3e486f387bb501369a0444e32313e59a7d _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -100,44 +100,25 @@ raise -class NoCapture: - def start_capturing(self): - pass - def stop_capturing(self): - pass - - def pop_outerr_to_orig(self): - pass - - def reset(self): - pass - - def readouterr(self): - return "", "" - +def maketmpfile(): + f = py.std.tempfile.TemporaryFile() + newf = dupfile(f, encoding="UTF-8") + f.close() + return newf class CaptureManager: def __init__(self, defaultmethod=None): self._method2capture = {} self._defaultmethod = defaultmethod - def _maketempfile(self): - f = py.std.tempfile.TemporaryFile() - newf = dupfile(f, encoding="UTF-8") - f.close() - return newf - def _getcapture(self, method): if method == "fd": - return StdCaptureFD( - out=self._maketempfile(), - err=self._maketempfile(), - ) + return StdCaptureBase(out=True, err=True, Capture=FDCapture) elif method == "sys": - return StdCapture(out=TextIO(), err=TextIO()) + return StdCaptureBase(out=True, err=True, Capture=SysCapture) elif method == "no": - return NoCapture() + return StdCaptureBase(out=False, err=False, in_=False) else: raise ValueError("unknown capturing method: %r" % method) @@ -277,8 +258,7 @@ """ if "capfd" in request._funcargs: raise request.raiseerror(error_capsysfderror) - return CaptureFixture(StdCapture) - + return CaptureFixture(SysCapture) def pytest_funcarg__capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes @@ -289,12 +269,13 @@ request.raiseerror(error_capsysfderror) if not hasattr(os, 'dup'): pytest.skip("capfd funcarg needs os.dup") - return CaptureFixture(StdCaptureFD) + return CaptureFixture(FDCapture) class CaptureFixture: def __init__(self, captureclass): - self._capture = captureclass(in_=False) + self._capture = StdCaptureBase(out=True, err=True, in_=False, + Capture=captureclass) def _start(self): self._capture.start_capturing() @@ -315,63 +296,6 @@ self._finalize() -class FDCapture: - """ Capture IO to/from a given os-level filedescriptor. """ - - def __init__(self, targetfd, tmpfile=None, patchsys=False): - """ save targetfd descriptor, and open a new - temporary file there. If no tmpfile is - specified a tempfile.Tempfile() will be opened - in text mode. - """ - self.targetfd = targetfd - if tmpfile is None and targetfd != 0: - # this code path is covered in the tests - # but not used by a regular pytest run - f = tempfile.TemporaryFile('wb+') - tmpfile = dupfile(f, encoding="UTF-8") - f.close() - self.tmpfile = tmpfile - self._savefd = os.dup(self.targetfd) - if patchsys: - self._oldsys = getattr(sys, patchsysdict[targetfd]) - - def start(self): - try: - os.fstat(self._savefd) - except OSError: - raise ValueError( - "saved filedescriptor not valid, " - "did you call start() twice?") - if self.targetfd == 0 and not self.tmpfile: - fd = os.open(os.devnull, os.O_RDONLY) - os.dup2(fd, 0) - os.close(fd) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], DontReadFromInput()) - else: - os.dup2(self.tmpfile.fileno(), self.targetfd) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], self.tmpfile) - - def done(self): - """ unpatch and clean up, returns the self.tmpfile (file object) - """ - os.dup2(self._savefd, self.targetfd) - os.close(self._savefd) - if self.targetfd != 0: - self.tmpfile.seek(0) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], self._oldsys) - return self.tmpfile - - def writeorg(self, data): - """ write a string to the original file descriptor - """ - if py.builtin._istext(data): - data = data.encode("utf8") # XXX use encoding of original stream - os.write(self._savefd, data) - def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): """ return a new open file object that's a duplicate of f @@ -421,6 +345,16 @@ class StdCaptureBase(object): + out = err = in_ = None + + def __init__(self, out=True, err=True, in_=True, Capture=None): + if in_: + self.in_ = Capture(0) + if out: + self.out = Capture(1) + if err: + self.err = Capture(2) + def reset(self): """ reset sys.stdout/stderr and return captured output as strings. """ if hasattr(self, '_reset'): @@ -436,6 +370,25 @@ errfile.close() return out, err + def start_capturing(self): + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def stop_capturing(self): + """ return (outfile, errfile) and stop capturing. """ + outfile = errfile = None + if self.out: + outfile = self.out.done() + if self.err: + errfile = self.err.done() + if self.in_: + self.in_.done() + return outfile, errfile + def pop_outerr_to_orig(self): """ pop current snapshot out/err capture and flush to orig streams. """ out, err = self.readouterr() @@ -444,61 +397,8 @@ if err: self.err.writeorg(err) - -class StdCaptureFD(StdCaptureBase): - """ This class allows to capture writes to FD1 and FD2 - and may connect a NULL file to FD0 (and prevent - reads from sys.stdin). If any of the 0,1,2 file descriptors - is invalid it will not be captured. - """ - def __init__(self, out=True, err=True, in_=True, patchsys=True): - if in_: - try: - self.in_ = FDCapture(0, tmpfile=None, patchsys=patchsys) - except OSError: - pass - if out: - tmpfile = None - if hasattr(out, 'write'): - tmpfile = out - try: - self.out = FDCapture(1, tmpfile=tmpfile, patchsys=patchsys) - except OSError: - pass - if err: - if hasattr(err, 'write'): - tmpfile = err - else: - tmpfile = None - try: - self.err = FDCapture(2, tmpfile=tmpfile, patchsys=patchsys) - except OSError: - pass - - def start_capturing(self): - if hasattr(self, 'in_'): - self.in_.start() - if hasattr(self, 'out'): - self.out.start() - if hasattr(self, 'err'): - self.err.start() - - #def pytest_sessionfinish(self): - # self.reset_capturings() - - def stop_capturing(self): - """ return (outfile, errfile) and stop capturing. """ - outfile = errfile = None - if hasattr(self, 'out') and not self.out.tmpfile.closed: - outfile = self.out.done() - if hasattr(self, 'err') and not self.err.tmpfile.closed: - errfile = self.err.done() - if hasattr(self, 'in_'): - self.in_.done() - return outfile, errfile - def readouterr(self): - """ return snapshot value of stdout/stderr capturings. """ + """ return snapshot unicode value of stdout/stderr capturings. """ return self._readsnapshot('out'), self._readsnapshot('err') def _readsnapshot(self, name): @@ -511,77 +411,87 @@ f.seek(0) res = f.read() enc = getattr(f, "encoding", None) - if enc: + if enc and isinstance(res, bytes): res = py.builtin._totext(res, enc, "replace") f.truncate(0) f.seek(0) return res -class TextCapture(TextIO): - def __init__(self, oldout): - super(TextCapture, self).__init__() - self._oldout = oldout + +class FDCapture: + """ Capture IO to/from a given os-level filedescriptor. """ + + def __init__(self, targetfd, tmpfile=None): + self.targetfd = targetfd + try: + self._savefd = os.dup(self.targetfd) + except OSError: + self.start = lambda: None + self.done = lambda: None + else: + if tmpfile is None: + if targetfd == 0: + tmpfile = open(os.devnull, "r") + else: + tmpfile = maketmpfile() + self.tmpfile = tmpfile + if targetfd in patchsysdict: + self._oldsys = getattr(sys, patchsysdict[targetfd]) + + def start(self): + """ Start capturing on targetfd using memorized tmpfile. """ + try: + os.fstat(self._savefd) + except OSError: + raise ValueError("saved filedescriptor not valid anymore") + targetfd = self.targetfd + os.dup2(self.tmpfile.fileno(), targetfd) + if hasattr(self, '_oldsys'): + subst = self.tmpfile if targetfd != 0 else DontReadFromInput() + setattr(sys, patchsysdict[targetfd], subst) + + def done(self): + """ stop capturing, restore streams, return original capture file, + seeked to position zero. """ + os.dup2(self._savefd, self.targetfd) + os.close(self._savefd) + if self.targetfd != 0: + self.tmpfile.seek(0) + if hasattr(self, '_oldsys'): + setattr(sys, patchsysdict[self.targetfd], self._oldsys) + return self.tmpfile def writeorg(self, data): - self._oldout.write(data) - self._oldout.flush() + """ write a string to the original file descriptor + """ + if py.builtin._istext(data): + data = data.encode("utf8") # XXX use encoding of original stream + os.write(self._savefd, data) -class StdCapture(StdCaptureBase): - """ This class allows to capture writes to sys.stdout|stderr "in-memory" - and will raise errors on tries to read from sys.stdin. It only - modifies sys.stdout|stderr|stdin attributes and does not - touch underlying File Descriptors (use StdCaptureFD for that). - """ - def __init__(self, out=True, err=True, in_=True): - self._oldout = sys.stdout - self._olderr = sys.stderr - self._oldin = sys.stdin - if out and not hasattr(out, 'file'): - out = TextCapture(self._oldout) - self.out = out - if err: - if not hasattr(err, 'write'): - err = TextCapture(self._olderr) - self.err = err - self.in_ = in_ +class SysCapture: + def __init__(self, fd): + name = patchsysdict[fd] + self._old = getattr(sys, name) + self.name = name + if name == "stdin": + self.tmpfile = DontReadFromInput() + else: + self.tmpfile = TextIO() - def start_capturing(self): - if self.out: - sys.stdout = self.out - if self.err: - sys.stderr = self.err - if self.in_: - sys.stdin = self.in_ = DontReadFromInput() + def start(self): + setattr(sys, self.name, self.tmpfile) - def stop_capturing(self): - """ return (outfile, errfile) and stop capturing. """ - outfile = errfile = None - if self.out and not self.out.closed: - sys.stdout = self._oldout - outfile = self.out - outfile.seek(0) - if self.err and not self.err.closed: - sys.stderr = self._olderr - errfile = self.err - errfile.seek(0) - if self.in_: - sys.stdin = self._oldin - return outfile, errfile + def done(self): + setattr(sys, self.name, self._old) + if self.name != "stdin": + self.tmpfile.seek(0) + return self.tmpfile + def writeorg(self, data): + self._old.write(data) + self._old.flush() - def readouterr(self): - """ return snapshot value of stdout/stderr capturings. """ - out = err = "" - if self.out: - out = self.out.getvalue() - self.out.truncate(0) - self.out.seek(0) - if self.err: - err = self.err.getvalue() - self.err.truncate(0) - self.err.seek(0) - return out, err class DontReadFromInput: diff -r b7aa02ac2440be4829e6f9f5b983f149e169c4b1 -r 080d7f3e486f387bb501369a0444e32313e59a7d testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -4,6 +4,7 @@ import os import sys import py +import tempfile import pytest import contextlib @@ -44,6 +45,13 @@ +def StdCaptureFD(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture) + +def StdCapture(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture) + + class TestCaptureManager: def test_getmethod_default_no_fd(self, testdir, monkeypatch): config = testdir.parseconfig(testdir.tmpdir) @@ -75,7 +83,7 @@ @needsosdup @pytest.mark.parametrize("method", ['no', 'fd', 'sys']) def test_capturing_basic_api(self, method): - capouter = capture.StdCaptureFD() + capouter = StdCaptureFD() old = sys.stdout, sys.stderr, sys.stdin try: capman = CaptureManager() @@ -99,7 +107,7 @@ @needsosdup def test_juggle_capturings(self, testdir): - capouter = capture.StdCaptureFD() + capouter = StdCaptureFD() try: #config = testdir.parseconfig(testdir.tmpdir) capman = CaptureManager() @@ -717,7 +725,7 @@ f.close() def test_stderr(self): - cap = capture.FDCapture(2, patchsys=True) + cap = capture.FDCapture(2) cap.start() print_("hello", file=sys.stderr) f = cap.done() @@ -727,7 +735,7 @@ def test_stdin(self, tmpfile): tmpfile.write(tobytes("3")) tmpfile.seek(0) - cap = capture.FDCapture(0, tmpfile=tmpfile) + cap = capture.FDCapture(0, tmpfile) cap.start() # check with os.read() directly instead of raw_input(), because # sys.stdin itself may be redirected (as pytest now does by default) @@ -753,7 +761,7 @@ class TestStdCapture: def getcapture(self, **kw): - cap = capture.StdCapture(**kw) + cap = StdCapture(**kw) cap.start_capturing() return cap @@ -878,7 +886,7 @@ pytestmark = needsosdup def getcapture(self, **kw): - cap = capture.StdCaptureFD(**kw) + cap = StdCaptureFD(**kw) cap.start_capturing() return cap @@ -899,18 +907,10 @@ def test_many(self, capfd): with lsof_check(): for i in range(10): - cap = capture.StdCaptureFD() + cap = StdCaptureFD() cap.reset() - at needsosdup -def test_stdcapture_fd_tmpfile(tmpfile): - capfd = capture.StdCaptureFD(out=tmpfile) - os.write(1, "hello".encode("ascii")) - os.write(2, "world".encode("ascii")) - outf, errf = capfd.stop_capturing() - assert outf == tmpfile - class TestStdCaptureFDinvalidFD: pytestmark = needsosdup @@ -918,7 +918,10 @@ def test_stdcapture_fd_invalid_fd(self, testdir): testdir.makepyfile(""" import os - from _pytest.capture import StdCaptureFD + from _pytest import capture + def StdCaptureFD(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, + Capture=capture.FDCapture) def test_stdout(): os.close(1) cap = StdCaptureFD(out=True, err=False, in_=False) @@ -938,27 +941,12 @@ def test_capture_not_started_but_reset(): - capsys = capture.StdCapture() + capsys = StdCapture() capsys.stop_capturing() capsys.stop_capturing() capsys.reset() - at needsosdup -def test_capture_no_sys(): - capsys = capture.StdCapture() - try: - cap = capture.StdCaptureFD(patchsys=False) - cap.start_capturing() - sys.stdout.write("hello") - sys.stderr.write("world") - oswritebytes(1, "1") - oswritebytes(2, "2") - out, err = cap.reset() - assert out == "1" - assert err == "2" - finally: - capsys.reset() @needsosdup @@ -966,7 +954,7 @@ def test_fdcapture_tmpfile_remains_the_same(tmpfile, use): if not use: tmpfile = True - cap = capture.StdCaptureFD(out=False, err=tmpfile) + cap = StdCaptureFD(out=False, err=tmpfile) try: cap.start_capturing() capfile = cap.err.tmpfile @@ -977,7 +965,7 @@ assert capfile2 == capfile - at pytest.mark.parametrize('method', ['StdCapture', 'StdCaptureFD']) + at pytest.mark.parametrize('method', ['SysCapture', 'FDCapture']) def test_capturing_and_logging_fundamentals(testdir, method): if method == "StdCaptureFD" and not hasattr(os, 'dup'): pytest.skip("need os.dup") @@ -986,7 +974,8 @@ import sys, os import py, logging from _pytest import capture - cap = capture.%s(out=False, in_=False) + cap = capture.StdCaptureBase(out=False, in_=False, + Capture=capture.%s) cap.start_capturing() logging.warn("hello1") https://bitbucket.org/hpk42/pytest/commits/9d11be8dcb85/ Changeset: 9d11be8dcb85 User: hpk42 Date: 2014-03-28 07:11:25 Summary: simplify reset/stop_capturing and fix capturing wrt to capturing simple os.write() calls Affected #: 2 files diff -r 080d7f3e486f387bb501369a0444e32313e59a7d -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -6,7 +6,7 @@ import sys import os -import tempfile +from tempfile import TemporaryFile import contextlib import py @@ -101,12 +101,6 @@ -def maketmpfile(): - f = py.std.tempfile.TemporaryFile() - newf = dupfile(f, encoding="UTF-8") - f.close() - return newf - class CaptureManager: def __init__(self, defaultmethod=None): self._method2capture = {} @@ -137,7 +131,7 @@ def reset_capturings(self): for cap in self._method2capture.values(): cap.pop_outerr_to_orig() - cap.reset() + cap.stop_capturing() self._method2capture.clear() def resumecapture_item(self, item): @@ -274,15 +268,16 @@ class CaptureFixture: def __init__(self, captureclass): - self._capture = StdCaptureBase(out=True, err=True, in_=False, - Capture=captureclass) + self.captureclass = captureclass def _start(self): + self._capture = StdCaptureBase(out=True, err=True, in_=False, + Capture=self.captureclass) self._capture.start_capturing() def _finalize(self): if hasattr(self, '_capture'): - outerr = self._outerr = self._capture.reset() + outerr = self._outerr = self._capture.stop_capturing() del self._capture return outerr @@ -355,21 +350,6 @@ if err: self.err = Capture(2) - def reset(self): - """ reset sys.stdout/stderr and return captured output as strings. """ - if hasattr(self, '_reset'): - raise ValueError("was already reset") - self._reset = True - outfile, errfile = self.stop_capturing() - out, err = "", "" - if outfile and not outfile.closed: - out = outfile.read() - outfile.close() - if errfile and errfile != outfile and not errfile.closed: - err = errfile.read() - errfile.close() - return out, err - def start_capturing(self): if self.in_: self.in_.start() @@ -378,17 +358,6 @@ if self.err: self.err.start() - def stop_capturing(self): - """ return (outfile, errfile) and stop capturing. """ - outfile = errfile = None - if self.out: - outfile = self.out.done() - if self.err: - errfile = self.err.done() - if self.in_: - self.in_.done() - return outfile, errfile - def pop_outerr_to_orig(self): """ pop current snapshot out/err capture and flush to orig streams. """ out, err = self.readouterr() @@ -397,25 +366,27 @@ if err: self.err.writeorg(err) + def stop_capturing(self): + """ stop capturing and reset capturing streams """ + if hasattr(self, '_reset'): + raise ValueError("was already stopped") + self._reset = True + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + def readouterr(self): """ return snapshot unicode value of stdout/stderr capturings. """ return self._readsnapshot('out'), self._readsnapshot('err') def _readsnapshot(self, name): - try: - f = getattr(self, name).tmpfile - except AttributeError: - return '' - if f.tell() == 0: - return '' - f.seek(0) - res = f.read() - enc = getattr(f, "encoding", None) - if enc and isinstance(res, bytes): - res = py.builtin._totext(res, enc, "replace") - f.truncate(0) - f.seek(0) - return res + cap = getattr(self, name, None) + if cap is None: + return "" + return cap.snap() class FDCapture: @@ -433,11 +404,16 @@ if targetfd == 0: tmpfile = open(os.devnull, "r") else: - tmpfile = maketmpfile() + f = TemporaryFile() + with f: + tmpfile = dupfile(f, encoding="UTF-8") self.tmpfile = tmpfile if targetfd in patchsysdict: self._oldsys = getattr(sys, patchsysdict[targetfd]) + def __repr__(self): + return "" % (self.targetfd, self._savefd) + def start(self): """ Start capturing on targetfd using memorized tmpfile. """ try: @@ -450,16 +426,26 @@ subst = self.tmpfile if targetfd != 0 else DontReadFromInput() setattr(sys, patchsysdict[targetfd], subst) + def snap(self): + f = self.tmpfile + f.seek(0) + res = f.read() + if res: + enc = getattr(f, "encoding", None) + if enc and isinstance(res, bytes): + res = py.builtin._totext(res, enc, "replace") + f.truncate(0) + f.seek(0) + return res + def done(self): """ stop capturing, restore streams, return original capture file, seeked to position zero. """ os.dup2(self._savefd, self.targetfd) os.close(self._savefd) - if self.targetfd != 0: - self.tmpfile.seek(0) if hasattr(self, '_oldsys'): setattr(sys, patchsysdict[self.targetfd], self._oldsys) - return self.tmpfile + self.tmpfile.close() def writeorg(self, data): """ write a string to the original file descriptor @@ -482,18 +468,22 @@ def start(self): setattr(sys, self.name, self.tmpfile) + def snap(self): + f = self.tmpfile + res = f.getvalue() + f.truncate(0) + f.seek(0) + return res + def done(self): setattr(sys, self.name, self._old) - if self.name != "stdin": - self.tmpfile.seek(0) - return self.tmpfile + self.tmpfile.close() def writeorg(self, data): self._old.write(data) self._old.flush() - class DontReadFromInput: """Temporary stub class. Ideally when stdin is accessed, the capturing should be turned off, with possibly all data captured diff -r 080d7f3e486f387bb501369a0444e32313e59a7d -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -103,7 +103,7 @@ assert not out and not err capman.reset_capturings() finally: - capouter.reset() + capouter.stop_capturing() @needsosdup def test_juggle_capturings(self, testdir): @@ -127,7 +127,7 @@ finally: capman.reset_capturings() finally: - capouter.reset() + capouter.stop_capturing() @pytest.mark.parametrize("method", ['fd', 'sys']) @@ -696,17 +696,15 @@ cap = capture.FDCapture(fd) data = tobytes("hello") os.write(fd, data) - f = cap.done() - s = f.read() - f.close() + s = cap.snap() + cap.done() assert not s cap = capture.FDCapture(fd) cap.start() os.write(fd, data) - f = cap.done() - s = f.read() + s = cap.snap() + cap.done() assert s == "hello" - f.close() def test_simple_many(self, tmpfile): for i in range(10): @@ -720,16 +718,15 @@ def test_simple_fail_second_start(self, tmpfile): fd = tmpfile.fileno() cap = capture.FDCapture(fd) - f = cap.done() + cap.done() pytest.raises(ValueError, cap.start) - f.close() def test_stderr(self): cap = capture.FDCapture(2) cap.start() print_("hello", file=sys.stderr) - f = cap.done() - s = f.read() + s = cap.snap() + cap.done() assert s == "hello\n" def test_stdin(self, tmpfile): @@ -752,8 +749,8 @@ cap.writeorg(data2) finally: tmpfile.close() - f = cap.done() - scap = f.read() + scap = cap.snap() + cap.done() assert scap == totext(data1) stmp = open(tmpfile.name, 'rb').read() assert stmp == data2 @@ -769,17 +766,17 @@ cap = self.getcapture() sys.stdout.write("hello") sys.stderr.write("world") - outfile, errfile = cap.stop_capturing() - s = outfile.read() - assert s == "hello" - s = errfile.read() - assert s == "world" + out, err = cap.readouterr() + cap.stop_capturing() + assert out == "hello" + assert err == "world" def test_capturing_reset_simple(self): cap = self.getcapture() print("hello world") sys.stderr.write("hello error\n") - out, err = cap.reset() + out, err = cap.readouterr() + cap.stop_capturing() assert out == "hello world\n" assert err == "hello error\n" @@ -792,8 +789,9 @@ assert out == "hello world\n" assert err == "hello error\n" sys.stderr.write("error2") + out, err = cap.readouterr() finally: - out, err = cap.reset() + cap.stop_capturing() assert err == "error2" def test_capturing_readouterr_unicode(self): @@ -802,7 +800,7 @@ print ("hx\xc4\x85\xc4\x87") out, err = cap.readouterr() finally: - cap.reset() + cap.stop_capturing() assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") @pytest.mark.skipif('sys.version_info >= (3,)', @@ -813,13 +811,14 @@ print('\xa6') out, err = cap.readouterr() assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') - cap.reset() + cap.stop_capturing() def test_reset_twice_error(self): cap = self.getcapture() print ("hello") - out, err = cap.reset() - pytest.raises(ValueError, cap.reset) + out, err = cap.readouterr() + cap.stop_capturing() + pytest.raises(ValueError, cap.stop_capturing) assert out == "hello\n" assert not err @@ -833,7 +832,8 @@ sys.stderr = capture.TextIO() print ("not seen") sys.stderr.write("not seen\n") - out, err = cap.reset() + out, err = cap.readouterr() + cap.stop_capturing() assert out == "hello" assert err == "world" assert sys.stdout == oldout @@ -844,8 +844,10 @@ print ("cap1") cap2 = self.getcapture() print ("cap2") - out2, err2 = cap2.reset() - out1, err1 = cap1.reset() + out2, err2 = cap2.readouterr() + out1, err1 = cap1.readouterr() + cap2.stop_capturing() + cap1.stop_capturing() assert out1 == "cap1\n" assert out2 == "cap2\n" @@ -853,7 +855,8 @@ cap = self.getcapture(out=True, err=False) sys.stdout.write("hello") sys.stderr.write("world") - out, err = cap.reset() + out, err = cap.readouterr() + cap.stop_capturing() assert out == "hello" assert not err @@ -861,7 +864,8 @@ cap = self.getcapture(out=False, err=True) sys.stdout.write("hello") sys.stderr.write("world") - out, err = cap.reset() + out, err = cap.readouterr() + cap.stop_capturing() assert err == "world" assert not out @@ -869,7 +873,7 @@ old = sys.stdin cap = self.getcapture(in_=True) newstdin = sys.stdin - out, err = cap.reset() + cap.stop_capturing() assert newstdin != sys.stdin assert sys.stdin is old @@ -879,7 +883,7 @@ print ("XXX mechanisms") cap = self.getcapture() pytest.raises(IOError, "sys.stdin.read()") - out, err = cap.reset() + cap.stop_capturing() class TestStdCaptureFD(TestStdCapture): @@ -890,6 +894,20 @@ cap.start_capturing() return cap + def test_simple_only_fd(self, testdir): + testdir.makepyfile(""" + import os + def test_x(): + os.write(1, "hello\\n".encode("ascii")) + assert 0 + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *test_x* + *assert 0* + *Captured stdout* + """) + def test_intermingling(self): cap = self.getcapture() oswritebytes(1, "1") @@ -900,7 +918,8 @@ sys.stderr.write("b") sys.stderr.flush() oswritebytes(2, "c") - out, err = cap.reset() + out, err = cap.readouterr() + cap.stop_capturing() assert out == "123" assert err == "abc" @@ -908,7 +927,7 @@ with lsof_check(): for i in range(10): cap = StdCaptureFD() - cap.reset() + cap.stop_capturing() @@ -943,10 +962,6 @@ def test_capture_not_started_but_reset(): capsys = StdCapture() capsys.stop_capturing() - capsys.stop_capturing() - capsys.reset() - - @needsosdup @@ -960,7 +975,7 @@ capfile = cap.err.tmpfile cap.readouterr() finally: - cap.reset() + cap.stop_capturing() capfile2 = cap.err.tmpfile assert capfile2 == capfile https://bitbucket.org/hpk42/pytest/commits/f9dfeecab061/ Changeset: f9dfeecab061 User: hpk42 Date: 2014-03-28 07:13:08 Summary: simplify capturing funcarg handling Affected #: 1 file diff -r 9d11be8dcb85d493cdb1cd0612a2808be8152fee -r f9dfeecab0611e3f4a8f0f369ad0cf14beba3820 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -155,29 +155,23 @@ def suspendcapture(self, item=None): self.deactivate_funcargs() - if hasattr(self, '_capturing'): - method = self._capturing - del self._capturing + method = self.__dict__.pop("_capturing", None) + if method is not None: cap = self._method2capture.get(method) if cap is not None: return cap.readouterr() return "", "" def activate_funcargs(self, pyfuncitem): - funcargs = getattr(pyfuncitem, "funcargs", None) - if funcargs is not None: - for name, capfuncarg in funcargs.items(): - if name in ('capsys', 'capfd'): - assert not hasattr(self, '_capturing_funcarg') - self._capturing_funcarg = capfuncarg - capfuncarg._start() + capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg._start() + self._capfuncarg = capfuncarg def deactivate_funcargs(self): - capturing_funcarg = getattr(self, '_capturing_funcarg', None) - if capturing_funcarg: - outerr = capturing_funcarg._finalize() - del self._capturing_funcarg - return outerr + capfuncarg = self.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg.close() @pytest.mark.hookwrapper def pytest_make_collect_report(self, __multicall__, collector): @@ -210,7 +204,9 @@ @pytest.mark.hookwrapper def pytest_runtest_call(self, item): with self.item_capture_wrapper(item, "call"): + self.activate_funcargs(item) yield + #self.deactivate_funcargs() called from ctx's suspendcapture() @pytest.mark.hookwrapper def pytest_runtest_teardown(self, item): @@ -228,17 +224,8 @@ @contextlib.contextmanager def item_capture_wrapper(self, item, when): self.resumecapture_item(item) - if when == "call": - self.activate_funcargs(item) - yield - funcarg_outerr = self.deactivate_funcargs() - else: - yield - funcarg_outerr = None + yield out, err = self.suspendcapture(item) - if funcarg_outerr is not None: - out += funcarg_outerr[0] - err += funcarg_outerr[1] item.add_report_section(when, "out", out) item.add_report_section(when, "err", err) @@ -252,7 +239,8 @@ """ if "capfd" in request._funcargs: raise request.raiseerror(error_capsysfderror) - return CaptureFixture(SysCapture) + request.node._capfuncarg = c = CaptureFixture(SysCapture) + return c def pytest_funcarg__capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes @@ -263,7 +251,8 @@ request.raiseerror(error_capsysfderror) if not hasattr(os, 'dup'): pytest.skip("capfd funcarg needs os.dup") - return CaptureFixture(FDCapture) + request.node._capfuncarg = c = CaptureFixture(FDCapture) + return c class CaptureFixture: @@ -275,21 +264,17 @@ Capture=self.captureclass) self._capture.start_capturing() - def _finalize(self): - if hasattr(self, '_capture'): - outerr = self._outerr = self._capture.stop_capturing() - del self._capture - return outerr + def close(self): + cap = self.__dict__.pop("_capture", None) + if cap is not None: + cap.pop_outerr_to_orig() + cap.stop_capturing() def readouterr(self): try: return self._capture.readouterr() except AttributeError: - return self._outerr - - def close(self): - self._finalize() - + return "", "" def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): @@ -448,8 +433,7 @@ self.tmpfile.close() def writeorg(self, data): - """ write a string to the original file descriptor - """ + """ write to original file descriptor. """ if py.builtin._istext(data): data = data.encode("utf8") # XXX use encoding of original stream os.write(self._savefd, data) https://bitbucket.org/hpk42/pytest/commits/d3ebd61be2a1/ Changeset: d3ebd61be2a1 User: hpk42 Date: 2014-03-28 07:55:07 Summary: simplify some capturing tests Affected #: 1 file diff -r f9dfeecab0611e3f4a8f0f369ad0cf14beba3820 -r d3ebd61be2a14ea1bceb3acb7bb7caee98cae914 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -757,32 +757,35 @@ class TestStdCapture: + captureclass = staticmethod(StdCapture) + + @contextlib.contextmanager def getcapture(self, **kw): - cap = StdCapture(**kw) + cap = self.__class__.captureclass(**kw) cap.start_capturing() - return cap + try: + yield cap + finally: + cap.stop_capturing() def test_capturing_done_simple(self): - cap = self.getcapture() - sys.stdout.write("hello") - sys.stderr.write("world") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture() as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() assert out == "hello" assert err == "world" def test_capturing_reset_simple(self): - cap = self.getcapture() - print("hello world") - sys.stderr.write("hello error\n") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture() as cap: + print("hello world") + sys.stderr.write("hello error\n") + out, err = cap.readouterr() assert out == "hello world\n" assert err == "hello error\n" def test_capturing_readouterr(self): - cap = self.getcapture() - try: + with self.getcapture() as cap: print ("hello world") sys.stderr.write("hello error\n") out, err = cap.readouterr() @@ -790,34 +793,27 @@ assert err == "hello error\n" sys.stderr.write("error2") out, err = cap.readouterr() - finally: - cap.stop_capturing() assert err == "error2" def test_capturing_readouterr_unicode(self): - cap = self.getcapture() - try: + with self.getcapture() as cap: print ("hx\xc4\x85\xc4\x87") out, err = cap.readouterr() - finally: - cap.stop_capturing() assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") @pytest.mark.skipif('sys.version_info >= (3,)', reason='text output different for bytes on python3') def test_capturing_readouterr_decode_error_handling(self): - cap = self.getcapture() - # triggered a internal error in pytest - print('\xa6') - out, err = cap.readouterr() + with self.getcapture() as cap: + # triggered a internal error in pytest + print('\xa6') + out, err = cap.readouterr() assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') - cap.stop_capturing() def test_reset_twice_error(self): - cap = self.getcapture() - print ("hello") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture() as cap: + print ("hello") + out, err = cap.readouterr() pytest.raises(ValueError, cap.stop_capturing) assert out == "hello\n" assert not err @@ -825,55 +821,49 @@ def test_capturing_modify_sysouterr_in_between(self): oldout = sys.stdout olderr = sys.stderr - cap = self.getcapture() - sys.stdout.write("hello") - sys.stderr.write("world") - sys.stdout = capture.TextIO() - sys.stderr = capture.TextIO() - print ("not seen") - sys.stderr.write("not seen\n") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture() as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + sys.stdout = capture.TextIO() + sys.stderr = capture.TextIO() + print ("not seen") + sys.stderr.write("not seen\n") + out, err = cap.readouterr() assert out == "hello" assert err == "world" assert sys.stdout == oldout assert sys.stderr == olderr def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print ("cap1") - cap2 = self.getcapture() - print ("cap2") - out2, err2 = cap2.readouterr() - out1, err1 = cap1.readouterr() - cap2.stop_capturing() - cap1.stop_capturing() + with self.getcapture() as cap1: + print ("cap1") + with self.getcapture() as cap2: + print ("cap2") + out2, err2 = cap2.readouterr() + out1, err1 = cap1.readouterr() assert out1 == "cap1\n" assert out2 == "cap2\n" def test_just_out_capture(self): - cap = self.getcapture(out=True, err=False) - sys.stdout.write("hello") - sys.stderr.write("world") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture(out=True, err=False) as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() assert out == "hello" assert not err def test_just_err_capture(self): - cap = self.getcapture(out=False, err=True) - sys.stdout.write("hello") - sys.stderr.write("world") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture(out=False, err=True) as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() assert err == "world" assert not out def test_stdin_restored(self): old = sys.stdin - cap = self.getcapture(in_=True) - newstdin = sys.stdin - cap.stop_capturing() + with self.getcapture(in_=True) as cap: + newstdin = sys.stdin assert newstdin != sys.stdin assert sys.stdin is old @@ -881,18 +871,13 @@ print ("XXX this test may well hang instead of crashing") print ("XXX which indicates an error in the underlying capturing") print ("XXX mechanisms") - cap = self.getcapture() - pytest.raises(IOError, "sys.stdin.read()") - cap.stop_capturing() + with self.getcapture() as cap: + pytest.raises(IOError, "sys.stdin.read()") class TestStdCaptureFD(TestStdCapture): pytestmark = needsosdup - - def getcapture(self, **kw): - cap = StdCaptureFD(**kw) - cap.start_capturing() - return cap + captureclass = staticmethod(StdCaptureFD) def test_simple_only_fd(self, testdir): testdir.makepyfile(""" @@ -909,17 +894,16 @@ """) def test_intermingling(self): - cap = self.getcapture() - oswritebytes(1, "1") - sys.stdout.write(str(2)) - sys.stdout.flush() - oswritebytes(1, "3") - oswritebytes(2, "a") - sys.stderr.write("b") - sys.stderr.flush() - oswritebytes(2, "c") - out, err = cap.readouterr() - cap.stop_capturing() + with self.getcapture() as cap: + oswritebytes(1, "1") + sys.stdout.write(str(2)) + sys.stdout.flush() + oswritebytes(1, "3") + oswritebytes(2, "a") + sys.stderr.write("b") + sys.stderr.flush() + oswritebytes(2, "c") + out, err = cap.readouterr() assert out == "123" assert err == "abc" @@ -930,7 +914,6 @@ cap.stop_capturing() - class TestStdCaptureFDinvalidFD: pytestmark = needsosdup https://bitbucket.org/hpk42/pytest/commits/756cdf331fc8/ Changeset: 756cdf331fc8 User: hpk42 Date: 2014-03-28 09:27:44 Summary: merge Affected #: 16 files diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -1,12 +1,13 @@ """ - per-test stdout/stderr capturing mechanisms, - ``capsys`` and ``capfd`` function arguments. +per-test stdout/stderr capturing mechanism. + """ -# note: py.io capture was where copied from -# pylib 1.4.20.dev2 (rev 13d9af95547e) +from __future__ import with_statement + import sys import os -import tempfile +from tempfile import TemporaryFile +import contextlib import py import pytest @@ -58,8 +59,18 @@ method = "fd" if method == "fd" and not hasattr(os, "dup"): method = "sys" + pluginmanager = early_config.pluginmanager + if method != "no": + try: + sys.stdout.fileno() + except Exception: + dupped_stdout = sys.stdout + else: + dupped_stdout = dupfile(sys.stdout, buffering=1) + pluginmanager.register(dupped_stdout, "dupped_stdout") + #pluginmanager.add_shutdown(dupped_stdout.close) capman = CaptureManager(method) - early_config.pluginmanager.register(capman, "capturemanager") + pluginmanager.register(capman, "capturemanager") # make sure that capturemanager is properly reset at final shutdown def teardown(): @@ -68,13 +79,13 @@ except ValueError: pass - early_config.pluginmanager.add_shutdown(teardown) + pluginmanager.add_shutdown(teardown) # make sure logging does not raise exceptions at the end def silence_logging_at_shutdown(): if "logging" in sys.modules: sys.modules["logging"].raiseExceptions = False - early_config.pluginmanager.add_shutdown(silence_logging_at_shutdown) + pluginmanager.add_shutdown(silence_logging_at_shutdown) # finally trigger conftest loading but while capturing (issue93) capman.resumecapture() @@ -89,53 +100,19 @@ raise -def addouterr(rep, outerr): - for secname, content in zip(["out", "err"], outerr): - if content: - rep.sections.append(("Captured std%s" % secname, content)) - - -class NoCapture: - def startall(self): - pass - - def resume(self): - pass - - def reset(self): - pass - - def suspend(self): - return "", "" - class CaptureManager: def __init__(self, defaultmethod=None): self._method2capture = {} self._defaultmethod = defaultmethod - def _maketempfile(self): - f = py.std.tempfile.TemporaryFile() - newf = dupfile(f, encoding="UTF-8") - f.close() - return newf - - def _makestringio(self): - return TextIO() - def _getcapture(self, method): if method == "fd": - return StdCaptureFD( - out=self._maketempfile(), - err=self._maketempfile(), - ) + return StdCaptureBase(out=True, err=True, Capture=FDCapture) elif method == "sys": - return StdCapture( - out=self._makestringio(), - err=self._makestringio(), - ) + return StdCaptureBase(out=True, err=True, Capture=SysCapture) elif method == "no": - return NoCapture() + return StdCaptureBase(out=False, err=False, in_=False) else: raise ValueError("unknown capturing method: %r" % method) @@ -153,12 +130,12 @@ def reset_capturings(self): for cap in self._method2capture.values(): - cap.reset() + cap.pop_outerr_to_orig() + cap.stop_capturing() + self._method2capture.clear() def resumecapture_item(self, item): method = self._getmethod(item.config, item.fspath) - if not hasattr(item, 'outerr'): - item.outerr = ('', '') # we accumulate outerr on the item return self.resumecapture(method) def resumecapture(self, method=None): @@ -172,87 +149,85 @@ self._capturing = method if cap is None: self._method2capture[method] = cap = self._getcapture(method) - cap.startall() + cap.start_capturing() else: - cap.resume() + cap.pop_outerr_to_orig() def suspendcapture(self, item=None): self.deactivate_funcargs() - if hasattr(self, '_capturing'): - method = self._capturing + method = self.__dict__.pop("_capturing", None) + if method is not None: cap = self._method2capture.get(method) if cap is not None: - outerr = cap.suspend() - del self._capturing - if item: - outerr = (item.outerr[0] + outerr[0], - item.outerr[1] + outerr[1]) - return outerr - if hasattr(item, 'outerr'): - return item.outerr + return cap.readouterr() return "", "" def activate_funcargs(self, pyfuncitem): - funcargs = getattr(pyfuncitem, "funcargs", None) - if funcargs is not None: - for name, capfuncarg in funcargs.items(): - if name in ('capsys', 'capfd'): - assert not hasattr(self, '_capturing_funcarg') - self._capturing_funcarg = capfuncarg - capfuncarg._start() + capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg._start() + self._capfuncarg = capfuncarg def deactivate_funcargs(self): - capturing_funcarg = getattr(self, '_capturing_funcarg', None) - if capturing_funcarg: - outerr = capturing_funcarg._finalize() - del self._capturing_funcarg - return outerr + capfuncarg = self.__dict__.pop("_capfuncarg", None) + if capfuncarg is not None: + capfuncarg.close() + @pytest.mark.hookwrapper def pytest_make_collect_report(self, __multicall__, collector): method = self._getmethod(collector.config, collector.fspath) try: self.resumecapture(method) except ValueError: + yield # recursive collect, XXX refactor capturing # to allow for more lightweight recursive capturing return - try: - rep = __multicall__.execute() - finally: - outerr = self.suspendcapture() - addouterr(rep, outerr) - return rep + yield + out, err = self.suspendcapture() + # XXX getting the report from the ongoing hook call is a bit + # of a hack. We need to think about capturing during collection + # and find out if it's really needed fine-grained (per + # collector). + if __multicall__.results: + rep = __multicall__.results[0] + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + + @pytest.mark.hookwrapper + def pytest_runtest_setup(self, item): + with self.item_capture_wrapper(item, "setup"): + yield + + @pytest.mark.hookwrapper + def pytest_runtest_call(self, item): + with self.item_capture_wrapper(item, "call"): + self.activate_funcargs(item) + yield + #self.deactivate_funcargs() called from ctx's suspendcapture() + + @pytest.mark.hookwrapper + def pytest_runtest_teardown(self, item): + with self.item_capture_wrapper(item, "teardown"): + yield @pytest.mark.tryfirst - def pytest_runtest_setup(self, item): - self.resumecapture_item(item) + def pytest_keyboard_interrupt(self, excinfo): + self.reset_capturings() @pytest.mark.tryfirst - def pytest_runtest_call(self, item): + def pytest_internalerror(self, excinfo): + self.reset_capturings() + + @contextlib.contextmanager + def item_capture_wrapper(self, item, when): self.resumecapture_item(item) - self.activate_funcargs(item) - - @pytest.mark.tryfirst - def pytest_runtest_teardown(self, item): - self.resumecapture_item(item) - - def pytest_keyboard_interrupt(self, excinfo): - if hasattr(self, '_capturing'): - self.suspendcapture() - - @pytest.mark.tryfirst - def pytest_runtest_makereport(self, __multicall__, item, call): - funcarg_outerr = self.deactivate_funcargs() - rep = __multicall__.execute() - outerr = self.suspendcapture(item) - if funcarg_outerr is not None: - outerr = (outerr[0] + funcarg_outerr[0], - outerr[1] + funcarg_outerr[1]) - addouterr(rep, outerr) - if not rep.passed or rep.when == "teardown": - outerr = ('', '') - item.outerr = outerr - return rep + yield + out, err = self.suspendcapture(item) + item.add_report_section(when, "out", out) + item.add_report_section(when, "err", err) error_capsysfderror = "cannot use capsys and capfd at the same time" @@ -264,8 +239,8 @@ """ if "capfd" in request._funcargs: raise request.raiseerror(error_capsysfderror) - return CaptureFixture(StdCapture) - + request.node._capfuncarg = c = CaptureFixture(SysCapture) + return c def pytest_funcarg__capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes @@ -276,89 +251,30 @@ request.raiseerror(error_capsysfderror) if not hasattr(os, 'dup'): pytest.skip("capfd funcarg needs os.dup") - return CaptureFixture(StdCaptureFD) + request.node._capfuncarg = c = CaptureFixture(FDCapture) + return c class CaptureFixture: def __init__(self, captureclass): - self._capture = captureclass() + self.captureclass = captureclass def _start(self): - self._capture.startall() + self._capture = StdCaptureBase(out=True, err=True, in_=False, + Capture=self.captureclass) + self._capture.start_capturing() - def _finalize(self): - if hasattr(self, '_capture'): - outerr = self._outerr = self._capture.reset() - del self._capture - return outerr + def close(self): + cap = self.__dict__.pop("_capture", None) + if cap is not None: + cap.pop_outerr_to_orig() + cap.stop_capturing() def readouterr(self): try: return self._capture.readouterr() except AttributeError: - return self._outerr - - def close(self): - self._finalize() - - -class FDCapture: - """ Capture IO to/from a given os-level filedescriptor. """ - - def __init__(self, targetfd, tmpfile=None, patchsys=False): - """ save targetfd descriptor, and open a new - temporary file there. If no tmpfile is - specified a tempfile.Tempfile() will be opened - in text mode. - """ - self.targetfd = targetfd - if tmpfile is None and targetfd != 0: - f = tempfile.TemporaryFile('wb+') - tmpfile = dupfile(f, encoding="UTF-8") - f.close() - self.tmpfile = tmpfile - self._savefd = os.dup(self.targetfd) - if patchsys: - self._oldsys = getattr(sys, patchsysdict[targetfd]) - - def start(self): - try: - os.fstat(self._savefd) - except OSError: - raise ValueError( - "saved filedescriptor not valid, " - "did you call start() twice?") - if self.targetfd == 0 and not self.tmpfile: - fd = os.open(os.devnull, os.O_RDONLY) - os.dup2(fd, 0) - os.close(fd) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], DontReadFromInput()) - else: - os.dup2(self.tmpfile.fileno(), self.targetfd) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], self.tmpfile) - - def done(self): - """ unpatch and clean up, returns the self.tmpfile (file object) - """ - os.dup2(self._savefd, self.targetfd) - os.close(self._savefd) - if self.targetfd != 0: - self.tmpfile.seek(0) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], self._oldsys) - return self.tmpfile - - def writeorg(self, data): - """ write a string to the original file descriptor - """ - tempfp = tempfile.TemporaryFile() - try: - os.dup2(self._savefd, tempfp.fileno()) - tempfp.write(data) - finally: - tempfp.close() + return "", "" def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): @@ -408,185 +324,148 @@ return getattr(self._stream, name) -class Capture(object): - def reset(self): - """ reset sys.stdout/stderr and return captured output as strings. """ - if hasattr(self, '_reset'): - raise ValueError("was already reset") - self._reset = True - outfile, errfile = self.done(save=False) - out, err = "", "" - if outfile and not outfile.closed: - out = outfile.read() - outfile.close() - if errfile and errfile != outfile and not errfile.closed: - err = errfile.read() - errfile.close() - return out, err +class StdCaptureBase(object): + out = err = in_ = None - def suspend(self): - """ return current snapshot captures, memorize tempfiles. """ - outerr = self.readouterr() - outfile, errfile = self.done() - return outerr + def __init__(self, out=True, err=True, in_=True, Capture=None): + if in_: + self.in_ = Capture(0) + if out: + self.out = Capture(1) + if err: + self.err = Capture(2) - -class StdCaptureFD(Capture): - """ This class allows to capture writes to FD1 and FD2 - and may connect a NULL file to FD0 (and prevent - reads from sys.stdin). If any of the 0,1,2 file descriptors - is invalid it will not be captured. - """ - def __init__(self, out=True, err=True, in_=True, patchsys=True): - self._options = { - "out": out, - "err": err, - "in_": in_, - "patchsys": patchsys, - } - self._save() - - def _save(self): - in_ = self._options['in_'] - out = self._options['out'] - err = self._options['err'] - patchsys = self._options['patchsys'] - if in_: - try: - self.in_ = FDCapture( - 0, tmpfile=None, - patchsys=patchsys) - except OSError: - pass - if out: - tmpfile = None - if hasattr(out, 'write'): - tmpfile = out - try: - self.out = FDCapture( - 1, tmpfile=tmpfile, - patchsys=patchsys) - self._options['out'] = self.out.tmpfile - except OSError: - pass - if err: - if hasattr(err, 'write'): - tmpfile = err - else: - tmpfile = None - try: - self.err = FDCapture( - 2, tmpfile=tmpfile, - patchsys=patchsys) - self._options['err'] = self.err.tmpfile - except OSError: - pass - - def startall(self): - if hasattr(self, 'in_'): + def start_capturing(self): + if self.in_: self.in_.start() - if hasattr(self, 'out'): + if self.out: self.out.start() - if hasattr(self, 'err'): + if self.err: self.err.start() - def resume(self): - """ resume capturing with original temp files. """ - self.startall() + def pop_outerr_to_orig(self): + """ pop current snapshot out/err capture and flush to orig streams. """ + out, err = self.readouterr() + if out: + self.out.writeorg(out) + if err: + self.err.writeorg(err) - def done(self, save=True): - """ return (outfile, errfile) and stop capturing. """ - outfile = errfile = None - if hasattr(self, 'out') and not self.out.tmpfile.closed: - outfile = self.out.done() - if hasattr(self, 'err') and not self.err.tmpfile.closed: - errfile = self.err.done() - if hasattr(self, 'in_'): + def stop_capturing(self): + """ stop capturing and reset capturing streams """ + if hasattr(self, '_reset'): + raise ValueError("was already stopped") + self._reset = True + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: self.in_.done() - if save: - self._save() - return outfile, errfile def readouterr(self): - """ return snapshot value of stdout/stderr capturings. """ - out = self._readsnapshot('out') - err = self._readsnapshot('err') - return out, err + """ return snapshot unicode value of stdout/stderr capturings. """ + return self._readsnapshot('out'), self._readsnapshot('err') def _readsnapshot(self, name): - if hasattr(self, name): - f = getattr(self, name).tmpfile + cap = getattr(self, name, None) + if cap is None: + return "" + return cap.snap() + + +class FDCapture: + """ Capture IO to/from a given os-level filedescriptor. """ + + def __init__(self, targetfd, tmpfile=None): + self.targetfd = targetfd + try: + self._savefd = os.dup(self.targetfd) + except OSError: + self.start = lambda: None + self.done = lambda: None else: - return '' + if tmpfile is None: + if targetfd == 0: + tmpfile = open(os.devnull, "r") + else: + f = TemporaryFile() + with f: + tmpfile = dupfile(f, encoding="UTF-8") + self.tmpfile = tmpfile + if targetfd in patchsysdict: + self._oldsys = getattr(sys, patchsysdict[targetfd]) + def __repr__(self): + return "" % (self.targetfd, self._savefd) + + def start(self): + """ Start capturing on targetfd using memorized tmpfile. """ + try: + os.fstat(self._savefd) + except OSError: + raise ValueError("saved filedescriptor not valid anymore") + targetfd = self.targetfd + os.dup2(self.tmpfile.fileno(), targetfd) + if hasattr(self, '_oldsys'): + subst = self.tmpfile if targetfd != 0 else DontReadFromInput() + setattr(sys, patchsysdict[targetfd], subst) + + def snap(self): + f = self.tmpfile f.seek(0) res = f.read() - enc = getattr(f, "encoding", None) - if enc: - res = py.builtin._totext(res, enc, "replace") + if res: + enc = getattr(f, "encoding", None) + if enc and isinstance(res, bytes): + res = py.builtin._totext(res, enc, "replace") + f.truncate(0) + f.seek(0) + return res + + def done(self): + """ stop capturing, restore streams, return original capture file, + seeked to position zero. """ + os.dup2(self._savefd, self.targetfd) + os.close(self._savefd) + if hasattr(self, '_oldsys'): + setattr(sys, patchsysdict[self.targetfd], self._oldsys) + self.tmpfile.close() + + def writeorg(self, data): + """ write to original file descriptor. """ + if py.builtin._istext(data): + data = data.encode("utf8") # XXX use encoding of original stream + os.write(self._savefd, data) + + +class SysCapture: + def __init__(self, fd): + name = patchsysdict[fd] + self._old = getattr(sys, name) + self.name = name + if name == "stdin": + self.tmpfile = DontReadFromInput() + else: + self.tmpfile = TextIO() + + def start(self): + setattr(sys, self.name, self.tmpfile) + + def snap(self): + f = self.tmpfile + res = f.getvalue() f.truncate(0) f.seek(0) return res + def done(self): + setattr(sys, self.name, self._old) + self.tmpfile.close() -class StdCapture(Capture): - """ This class allows to capture writes to sys.stdout|stderr "in-memory" - and will raise errors on tries to read from sys.stdin. It only - modifies sys.stdout|stderr|stdin attributes and does not - touch underlying File Descriptors (use StdCaptureFD for that). - """ - def __init__(self, out=True, err=True, in_=True): - self._oldout = sys.stdout - self._olderr = sys.stderr - self._oldin = sys.stdin - if out and not hasattr(out, 'file'): - out = TextIO() - self.out = out - if err: - if not hasattr(err, 'write'): - err = TextIO() - self.err = err - self.in_ = in_ - - def startall(self): - if self.out: - sys.stdout = self.out - if self.err: - sys.stderr = self.err - if self.in_: - sys.stdin = self.in_ = DontReadFromInput() - - def done(self, save=True): - """ return (outfile, errfile) and stop capturing. """ - outfile = errfile = None - if self.out and not self.out.closed: - sys.stdout = self._oldout - outfile = self.out - outfile.seek(0) - if self.err and not self.err.closed: - sys.stderr = self._olderr - errfile = self.err - errfile.seek(0) - if self.in_: - sys.stdin = self._oldin - return outfile, errfile - - def resume(self): - """ resume capturing with original temp files. """ - self.startall() - - def readouterr(self): - """ return snapshot value of stdout/stderr capturings. """ - out = err = "" - if self.out: - out = self.out.getvalue() - self.out.truncate(0) - self.out.seek(0) - if self.err: - err = self.err.getvalue() - self.err.truncate(0) - self.err.seek(0) - return out, err + def writeorg(self, data): + self._old.write(data) + self._old.flush() class DontReadFromInput: diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -56,11 +56,15 @@ raise ValueError("not a string or argument list: %r" % (args,)) args = py.std.shlex.split(args) pluginmanager = get_plugin_manager() - if plugins: - for plugin in plugins: - pluginmanager.register(plugin) - return pluginmanager.hook.pytest_cmdline_parse( - pluginmanager=pluginmanager, args=args) + try: + if plugins: + for plugin in plugins: + pluginmanager.register(plugin) + return pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args) + except Exception: + pluginmanager.ensure_shutdown() + raise class PytestPluginManager(PluginManager): def __init__(self, hookspecs=[hookspec]): @@ -612,6 +616,9 @@ self.hook.pytest_logwarning(code=code, message=message, fslocation=None, nodeid=None) + def get_terminal_writer(self): + return self.pluginmanager.getplugin("terminalreporter")._tw + def pytest_cmdline_parse(self, pluginmanager, args): assert self == pluginmanager.config, (self, pluginmanager.config) self.parse(args) diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/core.py --- a/_pytest/core.py +++ b/_pytest/core.py @@ -240,18 +240,22 @@ pass l = [] last = [] + wrappers = [] for plugin in plugins: try: meth = getattr(plugin, attrname) - if hasattr(meth, 'tryfirst'): - last.append(meth) - elif hasattr(meth, 'trylast'): - l.insert(0, meth) - else: - l.append(meth) except AttributeError: continue + if hasattr(meth, 'hookwrapper'): + wrappers.append(meth) + elif hasattr(meth, 'tryfirst'): + last.append(meth) + elif hasattr(meth, 'trylast'): + l.insert(0, meth) + else: + l.append(meth) l.extend(last) + l.extend(wrappers) self._listattrcache[key] = list(l) return l @@ -272,6 +276,14 @@ class MultiCall: """ execute a call into multiple python functions/methods. """ + + class WrongHookWrapper(Exception): + """ a hook wrapper does not behave correctly. """ + def __init__(self, func, message): + Exception.__init__(self, func, message) + self.func = func + self.message = message + def __init__(self, methods, kwargs, firstresult=False): self.methods = list(methods) self.kwargs = kwargs @@ -283,16 +295,39 @@ return "" %(status, self.kwargs) def execute(self): - while self.methods: - method = self.methods.pop() - kwargs = self.getkwargs(method) - res = method(**kwargs) - if res is not None: - self.results.append(res) - if self.firstresult: - return res - if not self.firstresult: - return self.results + next_finalizers = [] + try: + while self.methods: + method = self.methods.pop() + kwargs = self.getkwargs(method) + if hasattr(method, "hookwrapper"): + it = method(**kwargs) + next = getattr(it, "next", None) + if next is None: + next = getattr(it, "__next__", None) + if next is None: + raise self.WrongHookWrapper(method, + "wrapper does not contain a yield") + res = next() + next_finalizers.append((method, next)) + else: + res = method(**kwargs) + if res is not None: + self.results.append(res) + if self.firstresult: + return res + if not self.firstresult: + return self.results + finally: + for method, fin in reversed(next_finalizers): + try: + fin() + except StopIteration: + pass + else: + raise self.WrongHookWrapper(method, + "wrapper contain more than one yield") + def getkwargs(self, method): kwargs = {} diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/genscript.py --- a/_pytest/genscript.py +++ b/_pytest/genscript.py @@ -60,6 +60,7 @@ def pytest_cmdline_main(config): genscript = config.getvalue("genscript") if genscript: + #tw = config.get_terminal_writer() tw = py.io.TerminalWriter() deps = ['py', '_pytest', 'pytest'] if sys.version_info < (2,7): diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -47,6 +47,8 @@ def pytest_cmdline_main(config): if config.option.version: + capman = config.pluginmanager.getplugin("capturemanager") + capman.reset_capturings() p = py.path.local(pytest.__file__) sys.stderr.write("This is pytest version %s, imported from %s\n" % (pytest.__version__, p)) @@ -62,7 +64,7 @@ return 0 def showhelp(config): - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() tw.write(config._parser.optparser.format_help()) tw.line() tw.line() diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -108,12 +108,14 @@ )) def _write_captured_output(self, report): - sec = dict(report.sections) - for name in ('out', 'err'): - content = sec.get("Captured std%s" % name) - if content: - tag = getattr(Junit, 'system-'+name) - self.append(tag(bin_xml_escape(content))) + for capname in ('out', 'err'): + allcontent = "" + for name, content in report.get_sections("Captured std%s" % + capname): + allcontent += content + if allcontent: + tag = getattr(Junit, 'system-'+capname) + self.append(tag(bin_xml_escape(allcontent))) def append(self, obj): self.tests[-1].append(obj) diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -233,6 +233,7 @@ # used for storing artificial fixturedefs for direct parametrization self._name2pseudofixturedef = {} + #self.extrainit() @property @@ -465,6 +466,14 @@ """ nextitem = None + def __init__(self, name, parent=None, config=None, session=None): + super(Item, self).__init__(name, parent, config, session) + self._report_sections = [] + + def add_report_section(self, when, key, content): + if content: + self._report_sections.append((when, key, content)) + def reportinfo(self): return self.fspath, None, "" diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -40,7 +40,7 @@ def pytest_cmdline_main(config): if config.option.markers: config.do_configure() - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() for line in config.getini("markers"): name, rest = line.split(":", 1) tw.write("@pytest.mark.%s:" % name, bold=True) diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -16,49 +16,36 @@ if config.getvalue("usepdb"): config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') - old_trace = py.std.pdb.set_trace + old = (py.std.pdb.set_trace, pytestPDB._pluginmanager) def fin(): - py.std.pdb.set_trace = old_trace + py.std.pdb.set_trace, pytestPDB._pluginmanager = old py.std.pdb.set_trace = pytest.set_trace + pytestPDB._pluginmanager = config.pluginmanager config._cleanup.append(fin) class pytestPDB: """ Pseudo PDB that defers to the real pdb. """ - item = None - collector = None + _pluginmanager = None def set_trace(self): """ invoke PDB set_trace debugging, dropping any IO capturing. """ frame = sys._getframe().f_back - item = self.item or self.collector - - if item is not None: - capman = item.config.pluginmanager.getplugin("capturemanager") - out, err = capman.suspendcapture() - if hasattr(item, 'outerr'): - item.outerr = (item.outerr[0] + out, item.outerr[1] + err) + capman = None + if self._pluginmanager is not None: + capman = self._pluginmanager.getplugin("capturemanager") + if capman: + capman.reset_capturings() tw = py.io.TerminalWriter() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") py.std.pdb.Pdb().set_trace(frame) -def pdbitem(item): - pytestPDB.item = item -pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem - - at pytest.mark.tryfirst -def pytest_make_collect_report(__multicall__, collector): - try: - pytestPDB.collector = collector - return __multicall__.execute() - finally: - pytestPDB.collector = None - -def pytest_runtest_makereport(): - pytestPDB.item = None class PdbInvoke: def pytest_exception_interact(self, node, call, report): + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.reset_capturings() return _enter_pdb(node, call.excinfo, report) def pytest_internalerror(self, excrepr, excinfo): diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -885,7 +885,7 @@ nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) nodeid.replace(session.fspath.sep, "/") - tw = py.io.TerminalWriter() + tw = config.get_terminal_writer() verbose = config.getvalue("verbose") fm = session._fixturemanager diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -135,14 +135,13 @@ self.when = when self.start = time() try: - try: - self.result = func() - except KeyboardInterrupt: - raise - except: - self.excinfo = py.code.ExceptionInfo() - finally: + self.result = func() + except KeyboardInterrupt: self.stop = time() + raise + except: + self.excinfo = py.code.ExceptionInfo() + self.stop = time() def __repr__(self): if self.excinfo: @@ -178,6 +177,11 @@ except UnicodeEncodeError: out.line("") + def get_sections(self, prefix): + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + passed = property(lambda x: x.outcome == "passed") failed = property(lambda x: x.outcome == "failed") skipped = property(lambda x: x.outcome == "skipped") @@ -191,6 +195,7 @@ duration = call.stop-call.start keywords = dict([(x,1) for x in item.keywords]) excinfo = call.excinfo + sections = [] if not call.excinfo: outcome = "passed" longrepr = None @@ -209,16 +214,18 @@ else: # exception in setup or teardown longrepr = item._repr_failure_py(excinfo, style=item.config.option.tbstyle) + for rwhen, key, content in item._report_sections: + sections.append(("Captured std%s %s" %(key, rwhen), content)) return TestReport(item.nodeid, item.location, keywords, outcome, longrepr, when, - duration=duration) + sections, duration) class TestReport(BaseReport): """ Basic test report object (also used for setup and teardown calls if they fail). """ - def __init__(self, nodeid, location, - keywords, outcome, longrepr, when, sections=(), duration=0, **extra): + def __init__(self, nodeid, location, keywords, outcome, + longrepr, when, sections=(), duration=0, **extra): #: normalized collection node id self.nodeid = nodeid @@ -286,7 +293,8 @@ class CollectReport(BaseReport): - def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra): + def __init__(self, nodeid, outcome, longrepr, result, + sections=(), **extra): self.nodeid = nodeid self.outcome = outcome self.longrepr = longrepr diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -36,7 +36,10 @@ def pytest_configure(config): config.option.verbose -= config.option.quiet - reporter = TerminalReporter(config, sys.stdout) + out = config.pluginmanager.getplugin("dupped_stdout") + #if out is None: + # out = sys.stdout + reporter = TerminalReporter(config, out) config.pluginmanager.register(reporter, 'terminalreporter') if config.option.debug or config.option.traceconfig: def mywriter(tags, args): @@ -44,6 +47,11 @@ reporter.write_line("[traceconfig] " + msg) config.trace.root.setprocessor("pytest:config", mywriter) +def get_terminal_writer(config): + tr = config.pluginmanager.getplugin("terminalreporter") + return tr._tw + + def getreportopt(config): reportopts = "" optvalue = config.option.report diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 bench/bench.py --- a/bench/bench.py +++ b/bench/bench.py @@ -4,8 +4,8 @@ import cProfile import pytest import pstats - script = sys.argv[1] if len(sys.argv) > 1 else "empty.py" - stats = cProfile.run('pytest.cmdline.main([%r])' % script, 'prof') + script = sys.argv[1:] if len(sys.argv) > 1 else "empty.py" + stats = cProfile.run('pytest.cmdline.main(%r)' % script, 'prof') p = pstats.Stats("prof") p.strip_dirs() p.sort_stats('cumulative') diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -4,6 +4,7 @@ import os import sys import py +import tempfile import pytest import contextlib @@ -44,6 +45,13 @@ +def StdCaptureFD(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture) + +def StdCapture(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture) + + class TestCaptureManager: def test_getmethod_default_no_fd(self, testdir, monkeypatch): config = testdir.parseconfig(testdir.tmpdir) @@ -75,7 +83,7 @@ @needsosdup @pytest.mark.parametrize("method", ['no', 'fd', 'sys']) def test_capturing_basic_api(self, method): - capouter = capture.StdCaptureFD() + capouter = StdCaptureFD() old = sys.stdout, sys.stderr, sys.stdin try: capman = CaptureManager() @@ -95,11 +103,11 @@ assert not out and not err capman.reset_capturings() finally: - capouter.reset() + capouter.stop_capturing() @needsosdup def test_juggle_capturings(self, testdir): - capouter = capture.StdCaptureFD() + capouter = StdCaptureFD() try: #config = testdir.parseconfig(testdir.tmpdir) capman = CaptureManager() @@ -119,7 +127,7 @@ finally: capman.reset_capturings() finally: - capouter.reset() + capouter.stop_capturing() @pytest.mark.parametrize("method", ['fd', 'sys']) @@ -282,9 +290,9 @@ "====* FAILURES *====", "____*____", "*test_capturing_outerr.py:8: ValueError", - "*--- Captured stdout ---*", + "*--- Captured stdout *call*", "1", - "*--- Captured stderr ---*", + "*--- Captured stderr *call*", "2", ]) @@ -688,17 +696,15 @@ cap = capture.FDCapture(fd) data = tobytes("hello") os.write(fd, data) - f = cap.done() - s = f.read() - f.close() + s = cap.snap() + cap.done() assert not s cap = capture.FDCapture(fd) cap.start() os.write(fd, data) - f = cap.done() - s = f.read() + s = cap.snap() + cap.done() assert s == "hello" - f.close() def test_simple_many(self, tmpfile): for i in range(10): @@ -712,22 +718,21 @@ def test_simple_fail_second_start(self, tmpfile): fd = tmpfile.fileno() cap = capture.FDCapture(fd) - f = cap.done() + cap.done() pytest.raises(ValueError, cap.start) - f.close() def test_stderr(self): - cap = capture.FDCapture(2, patchsys=True) + cap = capture.FDCapture(2) cap.start() print_("hello", file=sys.stderr) - f = cap.done() - s = f.read() + s = cap.snap() + cap.done() assert s == "hello\n" def test_stdin(self, tmpfile): tmpfile.write(tobytes("3")) tmpfile.seek(0) - cap = capture.FDCapture(0, tmpfile=tmpfile) + cap = capture.FDCapture(0, tmpfile) cap.start() # check with os.read() directly instead of raw_input(), because # sys.stdin itself may be redirected (as pytest now does by default) @@ -744,123 +749,121 @@ cap.writeorg(data2) finally: tmpfile.close() - f = cap.done() - scap = f.read() + scap = cap.snap() + cap.done() assert scap == totext(data1) stmp = open(tmpfile.name, 'rb').read() assert stmp == data2 class TestStdCapture: + captureclass = staticmethod(StdCapture) + + @contextlib.contextmanager def getcapture(self, **kw): - cap = capture.StdCapture(**kw) - cap.startall() - return cap + cap = self.__class__.captureclass(**kw) + cap.start_capturing() + try: + yield cap + finally: + cap.stop_capturing() def test_capturing_done_simple(self): - cap = self.getcapture() - sys.stdout.write("hello") - sys.stderr.write("world") - outfile, errfile = cap.done() - s = outfile.read() - assert s == "hello" - s = errfile.read() - assert s == "world" + with self.getcapture() as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() + assert out == "hello" + assert err == "world" def test_capturing_reset_simple(self): - cap = self.getcapture() - print("hello world") - sys.stderr.write("hello error\n") - out, err = cap.reset() + with self.getcapture() as cap: + print("hello world") + sys.stderr.write("hello error\n") + out, err = cap.readouterr() assert out == "hello world\n" assert err == "hello error\n" def test_capturing_readouterr(self): - cap = self.getcapture() - try: + with self.getcapture() as cap: print ("hello world") sys.stderr.write("hello error\n") out, err = cap.readouterr() assert out == "hello world\n" assert err == "hello error\n" sys.stderr.write("error2") - finally: - out, err = cap.reset() + out, err = cap.readouterr() assert err == "error2" def test_capturing_readouterr_unicode(self): - cap = self.getcapture() - try: + with self.getcapture() as cap: print ("hx\xc4\x85\xc4\x87") out, err = cap.readouterr() - finally: - cap.reset() assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") @pytest.mark.skipif('sys.version_info >= (3,)', reason='text output different for bytes on python3') def test_capturing_readouterr_decode_error_handling(self): - cap = self.getcapture() - # triggered a internal error in pytest - print('\xa6') - out, err = cap.readouterr() + with self.getcapture() as cap: + # triggered a internal error in pytest + print('\xa6') + out, err = cap.readouterr() assert out == py.builtin._totext('\ufffd\n', 'unicode-escape') def test_reset_twice_error(self): - cap = self.getcapture() - print ("hello") - out, err = cap.reset() - pytest.raises(ValueError, cap.reset) + with self.getcapture() as cap: + print ("hello") + out, err = cap.readouterr() + pytest.raises(ValueError, cap.stop_capturing) assert out == "hello\n" assert not err def test_capturing_modify_sysouterr_in_between(self): oldout = sys.stdout olderr = sys.stderr - cap = self.getcapture() - sys.stdout.write("hello") - sys.stderr.write("world") - sys.stdout = capture.TextIO() - sys.stderr = capture.TextIO() - print ("not seen") - sys.stderr.write("not seen\n") - out, err = cap.reset() + with self.getcapture() as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + sys.stdout = capture.TextIO() + sys.stderr = capture.TextIO() + print ("not seen") + sys.stderr.write("not seen\n") + out, err = cap.readouterr() assert out == "hello" assert err == "world" assert sys.stdout == oldout assert sys.stderr == olderr def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print ("cap1") - cap2 = self.getcapture() - print ("cap2") - out2, err2 = cap2.reset() - out1, err1 = cap1.reset() + with self.getcapture() as cap1: + print ("cap1") + with self.getcapture() as cap2: + print ("cap2") + out2, err2 = cap2.readouterr() + out1, err1 = cap1.readouterr() assert out1 == "cap1\n" assert out2 == "cap2\n" def test_just_out_capture(self): - cap = self.getcapture(out=True, err=False) - sys.stdout.write("hello") - sys.stderr.write("world") - out, err = cap.reset() + with self.getcapture(out=True, err=False) as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() assert out == "hello" assert not err def test_just_err_capture(self): - cap = self.getcapture(out=False, err=True) - sys.stdout.write("hello") - sys.stderr.write("world") - out, err = cap.reset() + with self.getcapture(out=False, err=True) as cap: + sys.stdout.write("hello") + sys.stderr.write("world") + out, err = cap.readouterr() assert err == "world" assert not out def test_stdin_restored(self): old = sys.stdin - cap = self.getcapture(in_=True) - newstdin = sys.stdin - out, err = cap.reset() + with self.getcapture(in_=True) as cap: + newstdin = sys.stdin assert newstdin != sys.stdin assert sys.stdin is old @@ -868,68 +871,47 @@ print ("XXX this test may well hang instead of crashing") print ("XXX which indicates an error in the underlying capturing") print ("XXX mechanisms") - cap = self.getcapture() - pytest.raises(IOError, "sys.stdin.read()") - out, err = cap.reset() - - def test_suspend_resume(self): - cap = self.getcapture(out=True, err=False, in_=False) - try: - print ("hello") - sys.stderr.write("error\n") - out, err = cap.suspend() - assert out == "hello\n" - assert not err - print ("in between") - sys.stderr.write("in between\n") - cap.resume() - print ("after") - sys.stderr.write("error_after\n") - finally: - out, err = cap.reset() - assert out == "after\n" - assert not err + with self.getcapture() as cap: + pytest.raises(IOError, "sys.stdin.read()") class TestStdCaptureFD(TestStdCapture): pytestmark = needsosdup + captureclass = staticmethod(StdCaptureFD) - def getcapture(self, **kw): - cap = capture.StdCaptureFD(**kw) - cap.startall() - return cap + def test_simple_only_fd(self, testdir): + testdir.makepyfile(""" + import os + def test_x(): + os.write(1, "hello\\n".encode("ascii")) + assert 0 + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *test_x* + *assert 0* + *Captured stdout* + """) def test_intermingling(self): - cap = self.getcapture() - oswritebytes(1, "1") - sys.stdout.write(str(2)) - sys.stdout.flush() - oswritebytes(1, "3") - oswritebytes(2, "a") - sys.stderr.write("b") - sys.stderr.flush() - oswritebytes(2, "c") - out, err = cap.reset() + with self.getcapture() as cap: + oswritebytes(1, "1") + sys.stdout.write(str(2)) + sys.stdout.flush() + oswritebytes(1, "3") + oswritebytes(2, "a") + sys.stderr.write("b") + sys.stderr.flush() + oswritebytes(2, "c") + out, err = cap.readouterr() assert out == "123" assert err == "abc" def test_many(self, capfd): with lsof_check(): for i in range(10): - cap = capture.StdCaptureFD() - cap.reset() - - - at needsosdup -def test_stdcapture_fd_tmpfile(tmpfile): - capfd = capture.StdCaptureFD(out=tmpfile) - try: - os.write(1, "hello".encode("ascii")) - os.write(2, "world".encode("ascii")) - outf, errf = capfd.done() - finally: - capfd.reset() - assert outf == tmpfile + cap = StdCaptureFD() + cap.stop_capturing() class TestStdCaptureFDinvalidFD: @@ -938,19 +920,22 @@ def test_stdcapture_fd_invalid_fd(self, testdir): testdir.makepyfile(""" import os - from _pytest.capture import StdCaptureFD + from _pytest import capture + def StdCaptureFD(out=True, err=True, in_=True): + return capture.StdCaptureBase(out, err, in_, + Capture=capture.FDCapture) def test_stdout(): os.close(1) cap = StdCaptureFD(out=True, err=False, in_=False) - cap.done() + cap.stop_capturing() def test_stderr(): os.close(2) cap = StdCaptureFD(out=False, err=True, in_=False) - cap.done() + cap.stop_capturing() def test_stdin(): os.close(0) cap = StdCaptureFD(out=False, err=False, in_=True) - cap.done() + cap.stop_capturing() """) result = testdir.runpytest("--capture=fd") assert result.ret == 0 @@ -958,27 +943,8 @@ def test_capture_not_started_but_reset(): - capsys = capture.StdCapture() - capsys.done() - capsys.done() - capsys.reset() - - - at needsosdup -def test_capture_no_sys(): - capsys = capture.StdCapture() - try: - cap = capture.StdCaptureFD(patchsys=False) - cap.startall() - sys.stdout.write("hello") - sys.stderr.write("world") - oswritebytes(1, "1") - oswritebytes(2, "2") - out, err = cap.reset() - assert out == "1" - assert err == "2" - finally: - capsys.reset() + capsys = StdCapture() + capsys.stop_capturing() @needsosdup @@ -986,19 +952,18 @@ def test_fdcapture_tmpfile_remains_the_same(tmpfile, use): if not use: tmpfile = True - cap = capture.StdCaptureFD(out=False, err=tmpfile) + cap = StdCaptureFD(out=False, err=tmpfile) try: - cap.startall() + cap.start_capturing() capfile = cap.err.tmpfile - cap.suspend() - cap.resume() + cap.readouterr() finally: - cap.reset() + cap.stop_capturing() capfile2 = cap.err.tmpfile assert capfile2 == capfile - at pytest.mark.parametrize('method', ['StdCapture', 'StdCaptureFD']) + at pytest.mark.parametrize('method', ['SysCapture', 'FDCapture']) def test_capturing_and_logging_fundamentals(testdir, method): if method == "StdCaptureFD" and not hasattr(os, 'dup'): pytest.skip("need os.dup") @@ -1007,23 +972,27 @@ import sys, os import py, logging from _pytest import capture - cap = capture.%s(out=False, in_=False) - cap.startall() + cap = capture.StdCaptureBase(out=False, in_=False, + Capture=capture.%s) + cap.start_capturing() logging.warn("hello1") - outerr = cap.suspend() + outerr = cap.readouterr() print ("suspend, captured %%s" %%(outerr,)) logging.warn("hello2") - cap.resume() + cap.pop_outerr_to_orig() logging.warn("hello3") - outerr = cap.suspend() + outerr = cap.readouterr() print ("suspend2, captured %%s" %% (outerr,)) """ % (method,)) result = testdir.runpython(p) - result.stdout.fnmatch_lines([ - "suspend, captured*hello1*", - "suspend2, captured*hello2*WARNING:root:hello3*", - ]) + result.stdout.fnmatch_lines(""" + suspend, captured*hello1* + suspend2, captured*WARNING:root:hello3* + """) + result.stderr.fnmatch_lines(""" + WARNING:root:hello2 + """) assert "atexit" not in result.stderr.str() diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_core.py --- a/testing/test_core.py +++ b/testing/test_core.py @@ -523,6 +523,95 @@ res = MultiCall([m1, m2], {}).execute() assert res == [1] + def test_hookwrapper(self): + l = [] + def m1(): + l.append("m1 init") + yield None + l.append("m1 finish") + m1.hookwrapper = True + + def m2(): + l.append("m2") + return 2 + res = MultiCall([m2, m1], {}).execute() + assert res == [2] + assert l == ["m1 init", "m2", "m1 finish"] + l[:] = [] + res = MultiCall([m2, m1], {}, firstresult=True).execute() + assert res == 2 + assert l == ["m1 init", "m2", "m1 finish"] + + def test_hookwrapper_order(self): + l = [] + def m1(): + l.append("m1 init") + yield 1 + l.append("m1 finish") + m1.hookwrapper = True + + def m2(): + l.append("m2 init") + yield 2 + l.append("m2 finish") + m2.hookwrapper = True + res = MultiCall([m2, m1], {}).execute() + assert res == [1, 2] + assert l == ["m1 init", "m2 init", "m2 finish", "m1 finish"] + + def test_listattr_hookwrapper_ordering(self): + class P1: + @pytest.mark.hookwrapper + def m(self): + return 17 + + class P2: + def m(self): + return 23 + + class P3: + @pytest.mark.tryfirst + def m(self): + return 19 + + pluginmanager = PluginManager() + p1 = P1() + p2 = P2() + p3 = P3() + pluginmanager.register(p1) + pluginmanager.register(p2) + pluginmanager.register(p3) + methods = pluginmanager.listattr('m') + assert methods == [p2.m, p3.m, p1.m] + ## listattr keeps a cache and deleting + ## a function attribute requires clearing it + #pluginmanager._listattrcache.clear() + #del P1.m.__dict__['tryfirst'] + + def test_hookwrapper_not_yield(self): + def m1(): + pass + m1.hookwrapper = True + + mc = MultiCall([m1], {}) + with pytest.raises(mc.WrongHookWrapper) as ex: + mc.execute() + assert ex.value.func == m1 + assert ex.value.message + + def test_hookwrapper_too_many_yield(self): + def m1(): + yield 1 + yield 2 + m1.hookwrapper = True + + mc = MultiCall([m1], {}) + with pytest.raises(mc.WrongHookWrapper) as ex: + mc.execute() + assert ex.value.func == m1 + assert ex.value.message + + class TestHookRelay: def test_happypath(self): pm = PluginManager() diff -r 3f7abe3da42704c757ea437f14bd8caf5b8f04c7 -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -478,10 +478,12 @@ path = testdir.tmpdir.join("test.xml") log = LogXML(str(path), None) ustr = py.builtin._totext("???!", "utf-8") - class report: + from _pytest.runner import BaseReport + class Report(BaseReport): longrepr = ustr sections = [] nodeid = "something" + report = Report() # hopefully this is not too brittle ... log.pytest_sessionstart() https://bitbucket.org/hpk42/pytest/commits/deee08c4eef3/ Changeset: deee08c4eef3 User: hpk42 Date: 2014-03-28 09:46:38 Summary: cleanup and refine issue412 test (still failing on py33) Affected #: 2 files diff -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -12,30 +12,7 @@ import py import pytest -try: - from io import StringIO -except ImportError: - from StringIO import StringIO - -try: - from io import BytesIO -except ImportError: - class BytesIO(StringIO): - def write(self, data): - if isinstance(data, unicode): - raise TypeError("not a byte value: %r" % (data,)) - StringIO.write(self, data) - -if sys.version_info < (3, 0): - class TextIO(StringIO): - def write(self, data): - if not isinstance(data, unicode): - enc = getattr(self, '_encoding', 'UTF-8') - data = unicode(data, enc, 'replace') - StringIO.write(self, data) -else: - TextIO = StringIO - +from py.io import TextIO patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} @@ -232,7 +209,8 @@ error_capsysfderror = "cannot use capsys and capfd at the same time" -def pytest_funcarg__capsys(request): + at pytest.fixture +def capsys(request): """enables capturing of writes to sys.stdout/sys.stderr and makes captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. @@ -242,7 +220,8 @@ request.node._capfuncarg = c = CaptureFixture(SysCapture) return c -def pytest_funcarg__capfd(request): + at pytest.fixture +def capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. diff -r 756cdf331fc809c566a71ca0a093f1c059e9cc10 -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -541,8 +541,8 @@ assert 'hello19' not in result.stdout.str() - at pytest.mark.xfail(sys.version_info>=(3,), reason='demonstrate #412') -def test_capture_badoutput(testdir): + at pytest.mark.xfail("sys.version_info >= (3,)") +def test_capture_badoutput_issue412(testdir): testdir.makepyfile(""" import os @@ -552,11 +552,12 @@ assert 0 """) result = testdir.runpytest('--cap=fd') - #this fails on python3 - fnmatch first for debugging - result.stdout.fnmatch_lines([ - '*1 failed*', - ]) - assert result.ret == 1 + result.stdout.fnmatch_lines(''' + *def test_func* + *assert 0* + *Captured* + *1 failed* + ''') def test_capture_early_option_parsing(testdir): @@ -616,7 +617,7 @@ def test_bytes_io(): - f = capture.BytesIO() + f = py.io.BytesIO() f.write(tobytes("hello")) pytest.raises(TypeError, "f.write(totext('hello'))") s = f.getvalue() https://bitbucket.org/hpk42/pytest/commits/116ac00cd354/ Changeset: 116ac00cd354 User: hpk42 Date: 2014-03-28 11:27:02 Summary: fix issue412 and other encoding issues. Streamline dupfile() into a new more thoughtful safe_text_dupfile helper. Affected #: 3 files diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -13,6 +13,7 @@ import pytest from py.io import TextIO +unicode = py.builtin.text patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} @@ -38,12 +39,7 @@ method = "sys" pluginmanager = early_config.pluginmanager if method != "no": - try: - sys.stdout.fileno() - except Exception: - dupped_stdout = sys.stdout - else: - dupped_stdout = dupfile(sys.stdout, buffering=1) + dupped_stdout = safe_text_dupfile(sys.stdout, "wb") pluginmanager.register(dupped_stdout, "dupped_stdout") #pluginmanager.add_shutdown(dupped_stdout.close) capman = CaptureManager(method) @@ -256,51 +252,41 @@ return "", "" -def dupfile(f, mode=None, buffering=0, raising=False, encoding=None): - """ return a new open file object that's a duplicate of f - - mode is duplicated if not given, 'buffering' controls - buffer size (defaulting to no buffering) and 'raising' - defines whether an exception is raised when an incompatible - file object is passed in (if raising is False, the file - object itself will be returned) +def safe_text_dupfile(f, mode, default_encoding="UTF8"): + """ return a open text file object that's a duplicate of f on the + FD-level if possible. """ + encoding = getattr(f, "encoding", None) try: fd = f.fileno() - mode = mode or f.mode - except AttributeError: - if raising: - raise - return f - newfd = os.dup(fd) - if sys.version_info >= (3, 0): - if encoding is not None: - mode = mode.replace("b", "") - buffering = True - return os.fdopen(newfd, mode, buffering, encoding, closefd=True) + except Exception: + if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"): + # we seem to have a text stream, let's just use it + return f else: - f = os.fdopen(newfd, mode, buffering) - if encoding is not None: - return EncodedFile(f, encoding) - return f + newfd = os.dup(fd) + if "b" not in mode: + mode += "b" + f = os.fdopen(newfd, mode, 0) # no buffering + return EncodedFile(f, encoding or default_encoding) class EncodedFile(object): - def __init__(self, _stream, encoding): - self._stream = _stream + def __init__(self, buffer, encoding): + self.buffer = buffer self.encoding = encoding def write(self, obj): if isinstance(obj, unicode): - obj = obj.encode(self.encoding) - self._stream.write(obj) + obj = obj.encode(self.encoding, "replace") + self.buffer.write(obj) def writelines(self, linelist): data = ''.join(linelist) self.write(data) def __getattr__(self, name): - return getattr(self._stream, name) + return getattr(self.buffer, name) class StdCaptureBase(object): @@ -344,13 +330,8 @@ def readouterr(self): """ return snapshot unicode value of stdout/stderr capturings. """ - return self._readsnapshot('out'), self._readsnapshot('err') - - def _readsnapshot(self, name): - cap = getattr(self, name, None) - if cap is None: - return "" - return cap.snap() + return (self.out.snap() if self.out is not None else "", + self.err.snap() if self.err is not None else "") class FDCapture: @@ -370,7 +351,7 @@ else: f = TemporaryFile() with f: - tmpfile = dupfile(f, encoding="UTF-8") + tmpfile = safe_text_dupfile(f, mode="wb+") self.tmpfile = tmpfile if targetfd in patchsysdict: self._oldsys = getattr(sys, patchsysdict[targetfd]) diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -104,6 +104,7 @@ self.startdir = self.curdir = py.path.local() if file is None: file = py.std.sys.stdout + #assert file.encoding self._tw = self.writer = py.io.TerminalWriter(file) if self.config.option.color == 'yes': self._tw.hasmarkup = True @@ -144,7 +145,8 @@ self._tw.write(content, **markup) def write_line(self, line, **markup): - line = str(line) + if not py.builtin._istext(line): + line = py.builtin.text(line, errors="replace") self.ensure_newline() self._tw.line(line, **markup) @@ -163,7 +165,7 @@ self._tw.line(msg, **kw) def pytest_internalerror(self, excrepr): - for line in str(excrepr).split("\n"): + for line in py.builtin.text(excrepr).split("\n"): self.write_line("INTERNALERROR> " + line) return 1 diff -r deee08c4eef3c15b09b1520bd72ba9fe990e60a7 -r 116ac00cd354f38cd0d55118581c31c1cb845bee testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -541,7 +541,6 @@ assert 'hello19' not in result.stdout.str() - at pytest.mark.xfail("sys.version_info >= (3,)") def test_capture_badoutput_issue412(testdir): testdir.makepyfile(""" import os @@ -571,8 +570,6 @@ assert 'hello19' in result.stdout.str() - at pytest.mark.xfail(sys.version_info >= (3, 0), reason='encoding issues') - at pytest.mark.xfail(sys.version_info < (2, 6), reason='test not run on py25') def test_capture_binary_output(testdir): testdir.makepyfile(r""" import pytest @@ -646,7 +643,7 @@ def test_dupfile(tmpfile): flist = [] for i in range(5): - nf = capture.dupfile(tmpfile, encoding="utf-8") + nf = capture.safe_text_dupfile(tmpfile, "wb") assert nf != tmpfile assert nf.fileno() != tmpfile.fileno() assert nf not in flist @@ -660,19 +657,17 @@ assert "01234" in repr(s) tmpfile.close() +def test_dupfile_on_bytesio(): + io = py.io.BytesIO() + f = capture.safe_text_dupfile(io, "wb") + f.write("hello") + assert io.getvalue() == b"hello" -def test_dupfile_no_mode(): - """ - dupfile should trap an AttributeError and return f if no mode is supplied. - """ - class SomeFileWrapper(object): - "An object with a fileno method but no mode attribute" - def fileno(self): - return 1 - tmpfile = SomeFileWrapper() - assert capture.dupfile(tmpfile) is tmpfile - with pytest.raises(AttributeError): - capture.dupfile(tmpfile, raising=True) +def test_dupfile_on_textio(): + io = py.io.TextIO() + f = capture.safe_text_dupfile(io, "wb") + f.write("hello") + assert io.getvalue() == "hello" @contextlib.contextmanager https://bitbucket.org/hpk42/pytest/commits/8004c01ca861/ Changeset: 8004c01ca861 User: hpk42 Date: 2014-03-28 11:29:29 Summary: add changelog entry: fix issue412: messing with stdout/stderr FD-level streams is now captured without crashes. Affected #: 1 file diff -r 116ac00cd354f38cd0d55118581c31c1cb845bee -r 8004c01ca861a7e1bd0a7939730e9660b59242cf CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,9 @@ - merge PR123: improved integration with mock.patch decorator on tests. +- fix issue412: messing with stdout/stderr FD-level streams is now + captured without crashes. + 2.5.2 ----------------------------------- https://bitbucket.org/hpk42/pytest/commits/529b5e415bdb/ Changeset: 529b5e415bdb User: hpk42 Date: 2014-04-01 14:19:52 Summary: rename StdCaptureBase to MultiCapture Affected #: 2 files diff -r 8004c01ca861a7e1bd0a7939730e9660b59242cf -r 529b5e415bdbbc441e83653c0a0143e22398768b _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -81,11 +81,11 @@ def _getcapture(self, method): if method == "fd": - return StdCaptureBase(out=True, err=True, Capture=FDCapture) + return MultiCapture(out=True, err=True, Capture=FDCapture) elif method == "sys": - return StdCaptureBase(out=True, err=True, Capture=SysCapture) + return MultiCapture(out=True, err=True, Capture=SysCapture) elif method == "no": - return StdCaptureBase(out=False, err=False, in_=False) + return MultiCapture(out=False, err=False, in_=False) else: raise ValueError("unknown capturing method: %r" % method) @@ -235,7 +235,7 @@ self.captureclass = captureclass def _start(self): - self._capture = StdCaptureBase(out=True, err=True, in_=False, + self._capture = MultiCapture(out=True, err=True, in_=False, Capture=self.captureclass) self._capture.start_capturing() @@ -289,7 +289,7 @@ return getattr(self.buffer, name) -class StdCaptureBase(object): +class MultiCapture(object): out = err = in_ = None def __init__(self, out=True, err=True, in_=True, Capture=None): diff -r 8004c01ca861a7e1bd0a7939730e9660b59242cf -r 529b5e415bdbbc441e83653c0a0143e22398768b testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -46,10 +46,10 @@ def StdCaptureFD(out=True, err=True, in_=True): - return capture.StdCaptureBase(out, err, in_, Capture=capture.FDCapture) + return capture.MultiCapture(out, err, in_, Capture=capture.FDCapture) def StdCapture(out=True, err=True, in_=True): - return capture.StdCaptureBase(out, err, in_, Capture=capture.SysCapture) + return capture.MultiCapture(out, err, in_, Capture=capture.SysCapture) class TestCaptureManager: @@ -918,7 +918,7 @@ import os from _pytest import capture def StdCaptureFD(out=True, err=True, in_=True): - return capture.StdCaptureBase(out, err, in_, + return capture.MultiCapture(out, err, in_, Capture=capture.FDCapture) def test_stdout(): os.close(1) @@ -958,6 +958,25 @@ capfile2 = cap.err.tmpfile assert capfile2 == capfile + at needsosdup +def test_close_and_capture_again(testdir): + testdir.makepyfile(""" + import os + def test_close(): + os.close(1) + def test_capture_again(): + os.write(1, "hello\\n") + assert 0 + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *test_capture_again* + *assert 0* + *stdout* + *hello* + """) + + @pytest.mark.parametrize('method', ['SysCapture', 'FDCapture']) def test_capturing_and_logging_fundamentals(testdir, method): @@ -968,7 +987,7 @@ import sys, os import py, logging from _pytest import capture - cap = capture.StdCaptureBase(out=False, in_=False, + cap = capture.MultiCapture(out=False, in_=False, Capture=capture.%s) cap.start_capturing() https://bitbucket.org/hpk42/pytest/commits/cf556c9a4df3/ Changeset: cf556c9a4df3 User: hpk42 Date: 2014-04-01 14:19:55 Summary: avoid some redundancy by using SysCapture from FDCapture for manipulating sys.std{out,in,err} Affected #: 2 files diff -r 529b5e415bdbbc441e83653c0a0143e22398768b -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -333,6 +333,8 @@ return (self.out.snap() if self.out is not None else "", self.err.snap() if self.err is not None else "") +class NoCapture: + __init__ = start = done = lambda *args: None class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ @@ -340,36 +342,38 @@ def __init__(self, targetfd, tmpfile=None): self.targetfd = targetfd try: - self._savefd = os.dup(self.targetfd) + self.targetfd_save = os.dup(self.targetfd) except OSError: self.start = lambda: None self.done = lambda: None else: - if tmpfile is None: - if targetfd == 0: - tmpfile = open(os.devnull, "r") - else: + if targetfd == 0: + assert not tmpfile, "cannot set tmpfile with stdin" + tmpfile = open(os.devnull, "r") + self.syscapture = SysCapture(targetfd) + else: + if tmpfile is None: f = TemporaryFile() with f: tmpfile = safe_text_dupfile(f, mode="wb+") + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, tmpfile) + else: + self.syscapture = NoCapture() self.tmpfile = tmpfile - if targetfd in patchsysdict: - self._oldsys = getattr(sys, patchsysdict[targetfd]) + self.tmpfile_fd = tmpfile.fileno() def __repr__(self): - return "" % (self.targetfd, self._savefd) + return "" % (self.targetfd, self.targetfd_save) def start(self): """ Start capturing on targetfd using memorized tmpfile. """ try: - os.fstat(self._savefd) - except OSError: + os.fstat(self.targetfd_save) + except (AttributeError, OSError): raise ValueError("saved filedescriptor not valid anymore") - targetfd = self.targetfd - os.dup2(self.tmpfile.fileno(), targetfd) - if hasattr(self, '_oldsys'): - subst = self.tmpfile if targetfd != 0 else DontReadFromInput() - setattr(sys, patchsysdict[targetfd], subst) + os.dup2(self.tmpfile_fd, self.targetfd) + self.syscapture.start() def snap(self): f = self.tmpfile @@ -386,28 +390,38 @@ def done(self): """ stop capturing, restore streams, return original capture file, seeked to position zero. """ - os.dup2(self._savefd, self.targetfd) - os.close(self._savefd) - if hasattr(self, '_oldsys'): - setattr(sys, patchsysdict[self.targetfd], self._oldsys) + targetfd_save = self.__dict__.pop("targetfd_save") + os.dup2(targetfd_save, self.targetfd) + os.close(targetfd_save) + self.syscapture.done() self.tmpfile.close() + def suspend(self): + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + + def resume(self): + self.syscapture.resume() + os.dup2(self.tmpfile_fd, self.targetfd) + def writeorg(self, data): """ write to original file descriptor. """ if py.builtin._istext(data): data = data.encode("utf8") # XXX use encoding of original stream - os.write(self._savefd, data) + os.write(self.targetfd_save, data) class SysCapture: - def __init__(self, fd): + def __init__(self, fd, tmpfile=None): name = patchsysdict[fd] self._old = getattr(sys, name) self.name = name - if name == "stdin": - self.tmpfile = DontReadFromInput() - else: - self.tmpfile = TextIO() + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = TextIO() + self.tmpfile = tmpfile def start(self): setattr(sys, self.name, self.tmpfile) @@ -421,8 +435,15 @@ def done(self): setattr(sys, self.name, self._old) + del self._old self.tmpfile.close() + def suspend(self): + setattr(sys, self.name, self._old) + + def resume(self): + setattr(sys, self.name, self.tmpfile) + def writeorg(self, data): self._old.write(data) self._old.flush() diff -r 529b5e415bdbbc441e83653c0a0143e22398768b -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -726,15 +726,11 @@ assert s == "hello\n" def test_stdin(self, tmpfile): - tmpfile.write(tobytes("3")) - tmpfile.seek(0) - cap = capture.FDCapture(0, tmpfile) + cap = capture.FDCapture(0) cap.start() - # check with os.read() directly instead of raw_input(), because - # sys.stdin itself may be redirected (as pytest now does by default) x = os.read(0, 100).strip() cap.done() - assert x == tobytes("3") + assert x == tobytes('') def test_writeorg(self, tmpfile): data1, data2 = tobytes("foo"), tobytes("bar") @@ -751,7 +747,37 @@ stmp = open(tmpfile.name, 'rb').read() assert stmp == data2 + def test_simple_resume_suspend(self, tmpfile): + with saved_fd(1): + cap = capture.FDCapture(1) + cap.start() + data = tobytes("hello") + os.write(1, data) + sys.stdout.write("whatever") + s = cap.snap() + assert s == "hellowhatever" + cap.suspend() + os.write(1, tobytes("world")) + sys.stdout.write("qlwkej") + assert not cap.snap() + cap.resume() + os.write(1, tobytes("but now")) + sys.stdout.write(" yes\n") + s = cap.snap() + assert s == "but now yes\n" + cap.suspend() + cap.done() + pytest.raises(AttributeError, cap.suspend) + at contextlib.contextmanager +def saved_fd(fd): + new_fd = os.dup(fd) + try: + yield + finally: + os.dup2(new_fd, fd) + + class TestStdCapture: captureclass = staticmethod(StdCapture) https://bitbucket.org/hpk42/pytest/commits/cccaaa5ff07f/ Changeset: cccaaa5ff07f User: hpk42 Date: 2014-04-01 14:19:58 Summary: introduce resume/suspend functionality for FDCapture and SysCapture, fixing problems with early bailouts (from argparse's parse() function e.g.) that wrote to stdout. Affected #: 2 files diff -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -46,13 +46,7 @@ pluginmanager.register(capman, "capturemanager") # make sure that capturemanager is properly reset at final shutdown - def teardown(): - try: - capman.reset_capturings() - except ValueError: - pass - - pluginmanager.add_shutdown(teardown) + pluginmanager.add_shutdown(capman.reset_capturings) # make sure logging does not raise exceptions at the end def silence_logging_at_shutdown(): @@ -124,16 +118,18 @@ self._method2capture[method] = cap = self._getcapture(method) cap.start_capturing() else: - cap.pop_outerr_to_orig() + cap.resume_capturing() def suspendcapture(self, item=None): self.deactivate_funcargs() method = self.__dict__.pop("_capturing", None) + outerr = "", "" if method is not None: cap = self._method2capture.get(method) if cap is not None: - return cap.readouterr() - return "", "" + outerr = cap.readouterr() + cap.suspend_capturing() + return outerr def activate_funcargs(self, pyfuncitem): capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) @@ -316,6 +312,18 @@ if err: self.err.writeorg(err) + def suspend_capturing(self): + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + + def resume_capturing(self): + if self.out: + self.out.resume() + if self.err: + self.err.resume() + def stop_capturing(self): """ stop capturing and reset capturing streams """ if hasattr(self, '_reset'): @@ -334,7 +342,7 @@ self.err.snap() if self.err is not None else "") class NoCapture: - __init__ = start = done = lambda *args: None + __init__ = start = done = suspend = resume = lambda *args: None class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ diff -r cf556c9a4df31a9b0b1d74a67b86ac2cc51f4006 -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc testing/acceptance_test.py --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -335,6 +335,12 @@ res = testdir.runpytest(p.basename) assert res.ret == 0 + def test_unknown_option(self, testdir): + result = testdir.runpytest("--qwlkej") + result.stderr.fnmatch_lines(""" + *unrecognized* + """) + class TestInvocationVariants: def test_earlyinit(self, testdir): https://bitbucket.org/hpk42/pytest/commits/d9cd8600a979/ Changeset: d9cd8600a979 User: hpk42 Date: 2014-04-01 14:32:12 Summary: remove non-documented per-conftest capturing option and simplify/refactor all code accordingly. Also make capturing more robust against tests closing FD1/2 and against pdb.set_trace() calls. Affected #: 4 files diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -21,9 +21,10 @@ def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( - '--capture', action="store", default=None, + '--capture', action="store", + default="fd" if hasattr(os, "dup") else "sys", metavar="method", choices=['fd', 'sys', 'no'], - help="per-test capturing method: one of fd (default)|sys|no.") + help="per-test capturing method: one of fd|sys|no.") group._addoption( '-s', action="store_const", const="no", dest="capture", help="shortcut for --capture=no.") @@ -32,16 +33,13 @@ @pytest.mark.tryfirst def pytest_load_initial_conftests(early_config, parser, args, __multicall__): ns = parser.parse_known_args(args) + pluginmanager = early_config.pluginmanager method = ns.capture - if not method: - method = "fd" - if method == "fd" and not hasattr(os, "dup"): - method = "sys" - pluginmanager = early_config.pluginmanager if method != "no": dupped_stdout = safe_text_dupfile(sys.stdout, "wb") pluginmanager.register(dupped_stdout, "dupped_stdout") #pluginmanager.add_shutdown(dupped_stdout.close) + capman = CaptureManager(method) pluginmanager.register(capman, "capturemanager") @@ -55,7 +53,7 @@ pluginmanager.add_shutdown(silence_logging_at_shutdown) # finally trigger conftest loading but while capturing (issue93) - capman.resumecapture() + capman.init_capturings() try: try: return __multicall__.execute() @@ -67,11 +65,9 @@ raise - class CaptureManager: - def __init__(self, defaultmethod=None): - self._method2capture = {} - self._defaultmethod = defaultmethod + def __init__(self, method): + self._method = method def _getcapture(self, method): if method == "fd": @@ -83,53 +79,27 @@ else: raise ValueError("unknown capturing method: %r" % method) - def _getmethod(self, config, fspath): - if config.option.capture: - method = config.option.capture - else: - try: - method = config._conftest.rget("option_capture", path=fspath) - except KeyError: - method = "fd" - if method == "fd" and not hasattr(os, 'dup'): # e.g. jython - method = "sys" - return method + def init_capturings(self): + assert not hasattr(self, "_capturing") + self._capturing = self._getcapture(self._method) + self._capturing.start_capturing() def reset_capturings(self): - for cap in self._method2capture.values(): + cap = self.__dict__.pop("_capturing", None) + if cap is not None: cap.pop_outerr_to_orig() cap.stop_capturing() - self._method2capture.clear() - def resumecapture_item(self, item): - method = self._getmethod(item.config, item.fspath) - return self.resumecapture(method) + def resumecapture(self): + self._capturing.resume_capturing() - def resumecapture(self, method=None): - if hasattr(self, '_capturing'): - raise ValueError( - "cannot resume, already capturing with %r" % - (self._capturing,)) - if method is None: - method = self._defaultmethod - cap = self._method2capture.get(method) - self._capturing = method - if cap is None: - self._method2capture[method] = cap = self._getcapture(method) - cap.start_capturing() - else: - cap.resume_capturing() - - def suspendcapture(self, item=None): + def suspendcapture(self, in_=False): self.deactivate_funcargs() - method = self.__dict__.pop("_capturing", None) - outerr = "", "" - if method is not None: - cap = self._method2capture.get(method) - if cap is not None: - outerr = cap.readouterr() - cap.suspend_capturing() - return outerr + cap = getattr(self, "_capturing", None) + if cap is not None: + outerr = cap.readouterr() + cap.suspend_capturing(in_=in_) + return outerr def activate_funcargs(self, pyfuncitem): capfuncarg = pyfuncitem.__dict__.pop("_capfuncarg", None) @@ -142,28 +112,20 @@ if capfuncarg is not None: capfuncarg.close() - @pytest.mark.hookwrapper + @pytest.mark.tryfirst def pytest_make_collect_report(self, __multicall__, collector): - method = self._getmethod(collector.config, collector.fspath) + if not isinstance(collector, pytest.File): + return + self.resumecapture() try: - self.resumecapture(method) - except ValueError: - yield - # recursive collect, XXX refactor capturing - # to allow for more lightweight recursive capturing - return - yield - out, err = self.suspendcapture() - # XXX getting the report from the ongoing hook call is a bit - # of a hack. We need to think about capturing during collection - # and find out if it's really needed fine-grained (per - # collector). - if __multicall__.results: - rep = __multicall__.results[0] - if out: - rep.sections.append(("Captured stdout", out)) - if err: - rep.sections.append(("Captured stderr", err)) + rep = __multicall__.execute() + finally: + out, err = self.suspendcapture() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + return rep @pytest.mark.hookwrapper def pytest_runtest_setup(self, item): @@ -192,9 +154,9 @@ @contextlib.contextmanager def item_capture_wrapper(self, item, when): - self.resumecapture_item(item) + self.resumecapture() yield - out, err = self.suspendcapture(item) + out, err = self.suspendcapture() item.add_report_section(when, "out", out) item.add_report_section(when, "err", err) @@ -238,14 +200,14 @@ def close(self): cap = self.__dict__.pop("_capture", None) if cap is not None: - cap.pop_outerr_to_orig() + self._outerr = cap.pop_outerr_to_orig() cap.stop_capturing() def readouterr(self): try: return self._capture.readouterr() except AttributeError: - return "", "" + return self._outerr def safe_text_dupfile(f, mode, default_encoding="UTF8"): @@ -311,18 +273,25 @@ self.out.writeorg(out) if err: self.err.writeorg(err) + return out, err - def suspend_capturing(self): + def suspend_capturing(self, in_=False): if self.out: self.out.suspend() if self.err: self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True def resume_capturing(self): if self.out: self.out.resume() if self.err: self.err.resume() + if hasattr(self, "_in_suspended"): + self.in_.resume() + del self._in_suspended def stop_capturing(self): """ stop capturing and reset capturing streams """ @@ -393,7 +362,8 @@ res = py.builtin._totext(res, enc, "replace") f.truncate(0) f.seek(0) - return res + return res + return '' def done(self): """ stop capturing, restore streams, return original capture file, diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce _pytest/pdb.py --- a/_pytest/pdb.py +++ b/_pytest/pdb.py @@ -34,7 +34,7 @@ if self._pluginmanager is not None: capman = self._pluginmanager.getplugin("capturemanager") if capman: - capman.reset_capturings() + capman.suspendcapture(in_=True) tw = py.io.TerminalWriter() tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") @@ -45,8 +45,8 @@ def pytest_exception_interact(self, node, call, report): capman = node.config.pluginmanager.getplugin("capturemanager") if capman: - capman.reset_capturings() - return _enter_pdb(node, call.excinfo, report) + capman.suspendcapture(in_=True) + _enter_pdb(node, call.excinfo, report) def pytest_internalerror(self, excrepr, excinfo): for line in str(excrepr).split("\n"): diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -53,79 +53,54 @@ class TestCaptureManager: - def test_getmethod_default_no_fd(self, testdir, monkeypatch): - config = testdir.parseconfig(testdir.tmpdir) - assert config.getvalue("capture") is None - capman = CaptureManager() + def test_getmethod_default_no_fd(self, monkeypatch): + from _pytest.capture import pytest_addoption + from _pytest.config import Parser + parser = Parser() + pytest_addoption(parser) + default = parser._groups[0].options[0].default + assert default == "fd" if hasattr(os, "dup") else "sys" + parser = Parser() monkeypatch.delattr(os, 'dup', raising=False) - try: - assert capman._getmethod(config, None) == "sys" - finally: - monkeypatch.undo() - - @pytest.mark.parametrize("mode", "no fd sys".split()) - def test_configure_per_fspath(self, testdir, mode): - config = testdir.parseconfig(testdir.tmpdir) - capman = CaptureManager() - hasfd = hasattr(os, 'dup') - if hasfd: - assert capman._getmethod(config, None) == "fd" - else: - assert capman._getmethod(config, None) == "sys" - - if not hasfd and mode == 'fd': - return - sub = testdir.tmpdir.mkdir("dir" + mode) - sub.ensure("__init__.py") - sub.join("conftest.py").write('option_capture = %r' % mode) - assert capman._getmethod(config, sub.join("test_hello.py")) == mode + pytest_addoption(parser) + assert parser._groups[0].options[0].default == "sys" @needsosdup - @pytest.mark.parametrize("method", ['no', 'fd', 'sys']) + @pytest.mark.parametrize("method", + ['no', 'sys', pytest.mark.skipif('not hasattr(os, "dup")', 'fd')]) def test_capturing_basic_api(self, method): capouter = StdCaptureFD() old = sys.stdout, sys.stderr, sys.stdin try: - capman = CaptureManager() - # call suspend without resume or start - outerr = capman.suspendcapture() + capman = CaptureManager(method) + capman.init_capturings() outerr = capman.suspendcapture() assert outerr == ("", "") - capman.resumecapture(method) + outerr = capman.suspendcapture() + assert outerr == ("", "") print ("hello") out, err = capman.suspendcapture() if method == "no": assert old == (sys.stdout, sys.stderr, sys.stdin) else: + assert not out + capman.resumecapture() + print ("hello") + out, err = capman.suspendcapture() + if method != "no": assert out == "hello\n" - capman.resumecapture(method) - out, err = capman.suspendcapture() - assert not out and not err capman.reset_capturings() finally: capouter.stop_capturing() @needsosdup - def test_juggle_capturings(self, testdir): + def test_init_capturing(self): capouter = StdCaptureFD() try: - #config = testdir.parseconfig(testdir.tmpdir) - capman = CaptureManager() - try: - capman.resumecapture("fd") - pytest.raises(ValueError, 'capman.resumecapture("fd")') - pytest.raises(ValueError, 'capman.resumecapture("sys")') - os.write(1, "hello\n".encode('ascii')) - out, err = capman.suspendcapture() - assert out == "hello\n" - capman.resumecapture("sys") - os.write(1, "hello\n".encode('ascii')) - py.builtin.print_("world", file=sys.stderr) - out, err = capman.suspendcapture() - assert not out - assert err == "world\n" - finally: - capman.reset_capturings() + capman = CaptureManager("fd") + capman.init_capturings() + pytest.raises(AssertionError, "capman.init_capturings()") + capman.reset_capturings() finally: capouter.stop_capturing() @@ -991,7 +966,7 @@ def test_close(): os.close(1) def test_capture_again(): - os.write(1, "hello\\n") + os.write(1, b"hello\\n") assert 0 """) result = testdir.runpytest() diff -r cccaaa5ff07fefada6e4929bdea33b1fa4edf7cc -r d9cd8600a979cd2442a2790edad12809d3d5d9ce testing/test_pdb.py --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -167,6 +167,26 @@ if child.isalive(): child.wait() + def test_set_trace_capturing_afterwards(self, testdir): + p1 = testdir.makepyfile(""" + import pdb + def test_1(): + pdb.set_trace() + def test_2(): + print ("hello") + assert 0 + """) + child = testdir.spawn_pytest(str(p1)) + child.expect("test_1") + child.send("c\n") + child.expect("test_2") + child.expect("Captured") + child.expect("hello") + child.sendeof() + child.read() + if child.isalive(): + child.wait() + @xfail_if_pdbpp_installed def test_pdb_interaction_doctest(self, testdir): p1 = testdir.makepyfile(""" https://bitbucket.org/hpk42/pytest/commits/d534ac2d68ec/ Changeset: d534ac2d68ec User: hpk42 Date: 2014-04-01 15:03:17 Summary: remove dupped_stdout logic and related changes, also simplify pytest_runtest_* calls to not use a contextlib with-decorator anymore. Affected #: 7 files diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,10 @@ - fix issue412: messing with stdout/stderr FD-level streams is now captured without crashes. +- simplified internal capturing mechanism and made it more robust + against tests or setups changing FD1/FD2, also better integrated + now with pytest.pdb() in single tests. + 2.5.2 ----------------------------------- diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -7,7 +7,6 @@ import sys import os from tempfile import TemporaryFile -import contextlib import py import pytest @@ -34,13 +33,9 @@ def pytest_load_initial_conftests(early_config, parser, args, __multicall__): ns = parser.parse_known_args(args) pluginmanager = early_config.pluginmanager - method = ns.capture - if method != "no": - dupped_stdout = safe_text_dupfile(sys.stdout, "wb") - pluginmanager.register(dupped_stdout, "dupped_stdout") - #pluginmanager.add_shutdown(dupped_stdout.close) - - capman = CaptureManager(method) + if ns.capture == "no": + return + capman = CaptureManager(ns.capture) pluginmanager.register(capman, "capturemanager") # make sure that capturemanager is properly reset at final shutdown @@ -129,20 +124,23 @@ @pytest.mark.hookwrapper def pytest_runtest_setup(self, item): - with self.item_capture_wrapper(item, "setup"): - yield + self.resumecapture() + yield + self.suspendcapture_item(item, "setup") @pytest.mark.hookwrapper def pytest_runtest_call(self, item): - with self.item_capture_wrapper(item, "call"): - self.activate_funcargs(item) - yield - #self.deactivate_funcargs() called from ctx's suspendcapture() + self.resumecapture() + self.activate_funcargs(item) + yield + #self.deactivate_funcargs() called from suspendcapture() + self.suspendcapture_item(item, "call") @pytest.mark.hookwrapper def pytest_runtest_teardown(self, item): - with self.item_capture_wrapper(item, "teardown"): - yield + self.resumecapture() + yield + self.suspendcapture_item(item, "teardown") @pytest.mark.tryfirst def pytest_keyboard_interrupt(self, excinfo): @@ -152,10 +150,7 @@ def pytest_internalerror(self, excinfo): self.reset_capturings() - @contextlib.contextmanager - def item_capture_wrapper(self, item, when): - self.resumecapture() - yield + def suspendcapture_item(self, item, when): out, err = self.suspendcapture() item.add_report_section(when, "out", out) item.add_report_section(when, "err", err) diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/genscript.py --- a/_pytest/genscript.py +++ b/_pytest/genscript.py @@ -60,7 +60,6 @@ def pytest_cmdline_main(config): genscript = config.getvalue("genscript") if genscript: - #tw = config.get_terminal_writer() tw = py.io.TerminalWriter() deps = ['py', '_pytest', 'pytest'] if sys.version_info < (2,7): diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -47,8 +47,6 @@ def pytest_cmdline_main(config): if config.option.version: - capman = config.pluginmanager.getplugin("capturemanager") - capman.reset_capturings() p = py.path.local(pytest.__file__) sys.stderr.write("This is pytest version %s, imported from %s\n" % (pytest.__version__, p)) @@ -64,7 +62,7 @@ return 0 def showhelp(config): - tw = config.get_terminal_writer() + tw = py.io.TerminalWriter() tw.write(config._parser.optparser.format_help()) tw.line() tw.line() diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -40,7 +40,7 @@ def pytest_cmdline_main(config): if config.option.markers: config.do_configure() - tw = config.get_terminal_writer() + tw = py.io.TerminalWriter() for line in config.getini("markers"): name, rest = line.split(":", 1) tw.write("@pytest.mark.%s:" % name, bold=True) diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -885,7 +885,7 @@ nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) nodeid.replace(session.fspath.sep, "/") - tw = config.get_terminal_writer() + tw = py.io.TerminalWriter() verbose = config.getvalue("verbose") fm = session._fixturemanager diff -r d9cd8600a979cd2442a2790edad12809d3d5d9ce -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -36,10 +36,7 @@ def pytest_configure(config): config.option.verbose -= config.option.quiet - out = config.pluginmanager.getplugin("dupped_stdout") - #if out is None: - # out = sys.stdout - reporter = TerminalReporter(config, out) + reporter = TerminalReporter(config, sys.stdout) config.pluginmanager.register(reporter, 'terminalreporter') if config.option.debug or config.option.traceconfig: def mywriter(tags, args): @@ -47,11 +44,6 @@ reporter.write_line("[traceconfig] " + msg) config.trace.root.setprocessor("pytest:config", mywriter) -def get_terminal_writer(config): - tr = config.pluginmanager.getplugin("terminalreporter") - return tr._tw - - def getreportopt(config): reportopts = "" optvalue = config.option.report @@ -104,7 +96,6 @@ self.startdir = self.curdir = py.path.local() if file is None: file = py.std.sys.stdout - #assert file.encoding self._tw = self.writer = py.io.TerminalWriter(file) if self.config.option.color == 'yes': self._tw.hasmarkup = True https://bitbucket.org/hpk42/pytest/commits/f40e7ec514f3/ Changeset: f40e7ec514f3 User: hpk42 Date: 2014-04-01 15:06:44 Summary: merge main Affected #: 4 files diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -40,3 +40,4 @@ Jurko Gospodneti? Marc Schlaich Christopher Gilling +Daniel Grana diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -26,11 +26,12 @@ - fix issue412: messing with stdout/stderr FD-level streams is now captured without crashes. +- fix issue483: trial/py33 works now properly. Thanks Daniel Grana for PR. + - simplified internal capturing mechanism and made it more robust against tests or setups changing FD1/FD2, also better integrated now with pytest.pdb() in single tests. - 2.5.2 ----------------------------------- diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -154,7 +154,7 @@ if isinstance(item, TestCaseFunction): if 'twisted.trial.unittest' in sys.modules: ut = sys.modules['twisted.python.failure'] - Failure__init__ = ut.Failure.__init__.im_func + Failure__init__ = ut.Failure.__init__ check_testcase_implements_trial_reporter() def excstore(self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None): diff -r d534ac2d68ec7de2d920d0bbddc6077c7f4d267d -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] distshare={homedir}/.tox/distshare -envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,trial +envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial [testenv] changedir=testing @@ -45,7 +45,7 @@ [testenv:py33-pexpect] changedir=testing -basepython=python2.7 +basepython=python3.3 deps={[testenv:py27-pexpect]deps} commands= py.test -rfsxX test_pdb.py test_terminal.py test_unittest.py @@ -61,12 +61,22 @@ py.test -n3 -rfsxX \ --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing} -[testenv:trial] +[testenv:py27-trial] changedir=. +basepython=python2.7 deps=twisted commands= py.test -rsxf \ --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py} + +[testenv:py33-trial] +changedir=. +basepython=python3.3 +deps={[testenv:py27-trial]deps} +commands= + py.test -rsxf \ + --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py} + [testenv:doctest] changedir=. commands=py.test --doctest-modules _pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 09:20:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:20:47 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140402072047.31500.75404@app02.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/88d27c9933f7/ Changeset: 88d27c9933f7 Branch: doc_plugins_pytest_sugar User: Marc Abramowitz Date: 2014-04-02 03:52:36 Summary: doc/en/plugins_index/index.txt: Update pytest-sugar Affected #: 1 file diff -r d4b093bc36df977c017b43286953286542c788cf -r 88d27c9933f7a54f6180253d3402ef69a8eb2222 doc/en/plugins_index/index.txt --- a/doc/en/plugins_index/index.txt +++ b/doc/en/plugins_index/index.txt @@ -107,8 +107,8 @@ :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner - `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 `link `_ py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). - :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 + `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). + :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 :target: https://github.com/Frozenball/pytest-sugar `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ `pytest-twisted `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 .. image:: github.png A twisted plugin for py.test. https://bitbucket.org/hpk42/pytest/commits/1f3a8d6f707b/ Changeset: 1f3a8d6f707b Branch: doc_plugins_pytest_sugar User: Marc Abramowitz Date: 2014-04-02 03:59:42 Summary: Update entry for pytest-sugar; GitHub icon instead of BitBucket, correct description Affected #: 1 file diff -r 88d27c9933f7a54f6180253d3402ef69a8eb2222 -r 1f3a8d6f707b7159b5d1bf0656bdaa4266dab336 doc/en/plugins_index/index.txt --- a/doc/en/plugins_index/index.txt +++ b/doc/en/plugins_index/index.txt @@ -107,7 +107,7 @@ :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner - `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). + `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: github.png changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 :target: https://github.com/Frozenball/pytest-sugar `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ https://bitbucket.org/hpk42/pytest/commits/d966b6d87ac5/ Changeset: d966b6d87ac5 User: hpk42 Date: 2014-04-02 09:20:42 Summary: Merged in msabramo/pytest/doc_plugins_pytest_sugar (pull request #141) doc/en/plugins_index/index.txt: Update pytest-sugar Affected #: 1 file diff -r f40e7ec514f3fcf6e59c3e13a44c3da4eb0959bb -r d966b6d87ac51aa38c68d73291fdfeb467dec4b9 doc/en/plugins_index/index.txt --- a/doc/en/plugins_index/index.txt +++ b/doc/en/plugins_index/index.txt @@ -107,8 +107,8 @@ :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner - `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 `link `_ py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). - :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 + `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: github.png changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). + :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 :target: https://github.com/Frozenball/pytest-sugar `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ `pytest-twisted `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 .. image:: github.png A twisted plugin for py.test. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 09:24:20 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:24:20 -0000 Subject: [Pytest-commit] commit/pytest: 5 new changesets Message-ID: <20140402072420.21460.91177@app03.ash-private.bitbucket.org> 5 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/edc9d6f6e76a/ Changeset: edc9d6f6e76a Branch: refactor_LsofFdLeakChecker User: Marc Abramowitz Date: 2014-04-01 19:15:27 Summary: testing/conftest.py: Refactor lsof fd leak checking Isolate the logic into one class to make easier to understand, more maintainable. This may aid in later plugging in an alternative implementation, such as one that uses psutil (https://bitbucket.org/hpk42/pytest/pull-request/137/use-psutil-to-detect-open-files-in-tests/diff) Affected #: 1 file diff -r d4b093bc36df977c017b43286953286542c788cf -r edc9d6f6e76ac45d9d1adadb710a239c030af28e testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -4,7 +4,23 @@ pytest_plugins = "pytester", import os, py -pid = os.getpid() + +class LsofFdLeakChecker(object): + def get_open_files(self): + out = self._exec_lsof() + open_files = self._parse_lsof_output(out) + return open_files + + def _exec_lsof(self): + pid = os.getpid() + return py.process.cmdexec("lsof -p %d" % pid) + + def _parse_lsof_output(self, out): + def isopen(line): + return ("REG" in line or "CHR" in line) and ( + "deleted" not in line and 'mem' not in line and "txt" not in line) + return [x for x in out.split("\n") if isopen(x)] + def pytest_addoption(parser): parser.addoption('--lsof', @@ -15,11 +31,12 @@ config._basedir = py.path.local() if config.getvalue("lsof"): try: - out = py.process.cmdexec("lsof -p %d" % pid) + config._fd_leak_checker = LsofFdLeakChecker() + config._openfiles = config._fd_leak_checker.get_open_files() except py.process.cmdexec.Error: pass else: - config._numfiles = len(getopenfiles(out)) + config._numfiles = len(config._openfiles) #def pytest_report_header(): # return "pid: %s" % os.getpid() @@ -31,8 +48,7 @@ return [x for x in out.split("\n") if isopen(x)] def check_open_files(config): - out2 = py.process.cmdexec("lsof -p %d" % pid) - lines2 = getopenfiles(out2) + lines2 = config._fd_leak_checker.get_open_files() if len(lines2) > config._numfiles + 3: error = [] error.append("***** %s FD leackage detected" % https://bitbucket.org/hpk42/pytest/commits/7a00688fef66/ Changeset: 7a00688fef66 Branch: refactor_LsofFdLeakChecker User: Marc Abramowitz Date: 2014-04-01 22:41:35 Summary: Improve LsofFdLeakChecker; more reliable and useful leak checking * Make it invoke lsof with options for machine-readable output * Parse out file descriptor and filename from lsof output * Draw attention to file descriptors now open that weren't open before Affected #: 1 file diff -r edc9d6f6e76ac45d9d1adadb710a239c030af28e -r 7a00688fef6649283f9316d1c0d0872c0af7bd71 testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -13,13 +13,24 @@ def _exec_lsof(self): pid = os.getpid() - return py.process.cmdexec("lsof -p %d" % pid) + return py.process.cmdexec("lsof -Ffn0 -p %d" % pid) def _parse_lsof_output(self, out): def isopen(line): - return ("REG" in line or "CHR" in line) and ( - "deleted" not in line and 'mem' not in line and "txt" not in line) - return [x for x in out.split("\n") if isopen(x)] + return line.startswith('f') and ( + "deleted" not in line and 'mem' not in line and "txt" not in line and 'cwd' not in line) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split('\0') + fd = int(fields[0][1:]) + filename = fields[1][1:] + if filename.startswith('/'): + open_files.append((fd, filename)) + + return open_files def pytest_addoption(parser): @@ -49,11 +60,16 @@ def check_open_files(config): lines2 = config._fd_leak_checker.get_open_files() - if len(lines2) > config._numfiles + 3: + new_fds = sorted(set([t[0] for t in lines2]) - set([t[0] for t in config._openfiles])) + open_files = [t for t in lines2 if t[0] in new_fds] + if open_files: error = [] - error.append("***** %s FD leackage detected" % - (len(lines2)-config._numfiles)) - error.extend(lines2) + error.append("***** %s FD leackage detected" % len(open_files)) + error.extend([str(f) for f in open_files]) + error.append("*** Before:") + error.extend([str(f) for f in config._openfiles]) + error.append("*** After:") + error.extend([str(f) for f in lines2]) error.append(error[0]) # update numfile so that the overall test run continuess config._numfiles = len(lines2) https://bitbucket.org/hpk42/pytest/commits/6751cb39a91f/ Changeset: 6751cb39a91f Branch: refactor_LsofFdLeakChecker User: Marc Abramowitz Date: 2014-04-01 23:13:11 Summary: testing/conftest.py: Reintialize config._openfiles for each test And no longer need getopenfiles or config._numfiles Affected #: 1 file diff -r 7a00688fef6649283f9316d1c0d0872c0af7bd71 -r 6751cb39a91f3fc7ed0c023db7fb9d4571ff1b6a testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -38,7 +38,8 @@ action="store_true", dest="lsof", default=False, help=("run FD checks if lsof is available")) -def pytest_configure(config): +def pytest_runtest_setup(item): + config = item.config config._basedir = py.path.local() if config.getvalue("lsof"): try: @@ -46,18 +47,10 @@ config._openfiles = config._fd_leak_checker.get_open_files() except py.process.cmdexec.Error: pass - else: - config._numfiles = len(config._openfiles) #def pytest_report_header(): # return "pid: %s" % os.getpid() -def getopenfiles(out): - def isopen(line): - return ("REG" in line or "CHR" in line) and ( - "deleted" not in line and 'mem' not in line and "txt" not in line) - return [x for x in out.split("\n") if isopen(x)] - def check_open_files(config): lines2 = config._fd_leak_checker.get_open_files() new_fds = sorted(set([t[0] for t in lines2]) - set([t[0] for t in config._openfiles])) @@ -71,13 +64,11 @@ error.append("*** After:") error.extend([str(f) for f in lines2]) error.append(error[0]) - # update numfile so that the overall test run continuess - config._numfiles = len(lines2) raise AssertionError("\n".join(error)) def pytest_runtest_teardown(item, __multicall__): item.config._basedir.chdir() - if hasattr(item.config, '_numfiles'): + if hasattr(item.config, '_openfiles'): x = __multicall__.execute() check_open_files(item.config) return x https://bitbucket.org/hpk42/pytest/commits/373fcb6b914d/ Changeset: 373fcb6b914d Branch: refactor_LsofFdLeakChecker User: Marc Abramowitz Date: 2014-04-02 00:36:54 Summary: Remove cast of fd to int and sorting Casting of fd can break for non-numeric fd (e.g.: "rtd" on Linux) and isn't necessary since we don't need to sort. Affected #: 1 file diff -r 6751cb39a91f3fc7ed0c023db7fb9d4571ff1b6a -r 373fcb6b914d6295c7b34ce33d464ab2aae27a9e testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -25,7 +25,7 @@ for line in out.split("\n"): if isopen(line): fields = line.split('\0') - fd = int(fields[0][1:]) + fd = fields[0][1:] filename = fields[1][1:] if filename.startswith('/'): open_files.append((fd, filename)) @@ -53,7 +53,7 @@ def check_open_files(config): lines2 = config._fd_leak_checker.get_open_files() - new_fds = sorted(set([t[0] for t in lines2]) - set([t[0] for t in config._openfiles])) + new_fds = set([t[0] for t in lines2]) - set([t[0] for t in config._openfiles]) open_files = [t for t in lines2 if t[0] in new_fds] if open_files: error = [] https://bitbucket.org/hpk42/pytest/commits/1cf062005b1c/ Changeset: 1cf062005b1c User: hpk42 Date: 2014-04-02 09:24:16 Summary: Merged in msabramo/pytest/refactor_LsofFdLeakChecker (pull request #138) testing/conftest.py: Refactor lsof fd leak checking Affected #: 1 file diff -r d966b6d87ac51aa38c68d73291fdfeb467dec4b9 -r 1cf062005b1c2743006cde11ee746b8dd53ea40f testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -4,48 +4,71 @@ pytest_plugins = "pytester", import os, py -pid = os.getpid() + +class LsofFdLeakChecker(object): + def get_open_files(self): + out = self._exec_lsof() + open_files = self._parse_lsof_output(out) + return open_files + + def _exec_lsof(self): + pid = os.getpid() + return py.process.cmdexec("lsof -Ffn0 -p %d" % pid) + + def _parse_lsof_output(self, out): + def isopen(line): + return line.startswith('f') and ( + "deleted" not in line and 'mem' not in line and "txt" not in line and 'cwd' not in line) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split('\0') + fd = fields[0][1:] + filename = fields[1][1:] + if filename.startswith('/'): + open_files.append((fd, filename)) + + return open_files + def pytest_addoption(parser): parser.addoption('--lsof', action="store_true", dest="lsof", default=False, help=("run FD checks if lsof is available")) -def pytest_configure(config): +def pytest_runtest_setup(item): + config = item.config config._basedir = py.path.local() if config.getvalue("lsof"): try: - out = py.process.cmdexec("lsof -p %d" % pid) + config._fd_leak_checker = LsofFdLeakChecker() + config._openfiles = config._fd_leak_checker.get_open_files() except py.process.cmdexec.Error: pass - else: - config._numfiles = len(getopenfiles(out)) #def pytest_report_header(): # return "pid: %s" % os.getpid() -def getopenfiles(out): - def isopen(line): - return ("REG" in line or "CHR" in line) and ( - "deleted" not in line and 'mem' not in line and "txt" not in line) - return [x for x in out.split("\n") if isopen(x)] - def check_open_files(config): - out2 = py.process.cmdexec("lsof -p %d" % pid) - lines2 = getopenfiles(out2) - if len(lines2) > config._numfiles + 3: + lines2 = config._fd_leak_checker.get_open_files() + new_fds = set([t[0] for t in lines2]) - set([t[0] for t in config._openfiles]) + open_files = [t for t in lines2 if t[0] in new_fds] + if open_files: error = [] - error.append("***** %s FD leackage detected" % - (len(lines2)-config._numfiles)) - error.extend(lines2) + error.append("***** %s FD leackage detected" % len(open_files)) + error.extend([str(f) for f in open_files]) + error.append("*** Before:") + error.extend([str(f) for f in config._openfiles]) + error.append("*** After:") + error.extend([str(f) for f in lines2]) error.append(error[0]) - # update numfile so that the overall test run continuess - config._numfiles = len(lines2) raise AssertionError("\n".join(error)) def pytest_runtest_teardown(item, __multicall__): item.config._basedir.chdir() - if hasattr(item.config, '_numfiles'): + if hasattr(item.config, '_openfiles'): x = __multicall__.execute() check_open_files(item.config) return x Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 09:30:59 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:30:59 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140402073059.9259.56126@app10.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/876494025a0b/ Changeset: 876494025a0b Branch: test_writeorg_close_tempfile User: Marc Abramowitz Date: 2014-03-31 17:25:35 Summary: test_writeorg: Close a tempfile Affected #: 1 file diff -r d4b093bc36df977c017b43286953286542c788cf -r 876494025a0b0e05ef38ec1ecb61d0591df99b35 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -747,8 +747,9 @@ f = cap.done() scap = f.read() assert scap == totext(data1) - stmp = open(tmpfile.name, 'rb').read() - assert stmp == data2 + with open(tmpfile.name, 'rb') as stmp_file: + stmp = stmp_file.read() + assert stmp == data2 class TestStdCapture: https://bitbucket.org/hpk42/pytest/commits/61109199d976/ Changeset: 61109199d976 User: hpk42 Date: 2014-04-02 09:30:55 Summary: Merged in msabramo/pytest/test_writeorg_close_tempfile (pull request #136) test_writeorg: Close a tempfile Affected #: 1 file diff -r 1cf062005b1c2743006cde11ee746b8dd53ea40f -r 61109199d97671367bf6498827b349afccb7a154 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -719,8 +719,9 @@ scap = cap.snap() cap.done() assert scap == totext(data1) - stmp = open(tmpfile.name, 'rb').read() - assert stmp == data2 + with open(tmpfile.name, 'rb') as stmp_file: + stmp = stmp_file.read() + assert stmp == data2 def test_simple_resume_suspend(self, tmpfile): with saved_fd(1): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 09:51:36 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:51:36 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: add changelog entry for some PRs: improvements to pytest's own Message-ID: <20140402075136.20532.84853@app12.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/8d04808c1e92/ Changeset: 8d04808c1e92 User: hpk42 Date: 2014-04-02 09:51:24 Summary: add changelog entry for some PRs: improvements to pytest's own test-suite leakage detection, courtesy of PRs from Marc Abramowitz Affected #: 2 files diff -r 61109199d97671367bf6498827b349afccb7a154 -r 8d04808c1e928179c11974741428a418a9378d37 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,9 @@ against tests or setups changing FD1/FD2, also better integrated now with pytest.pdb() in single tests. +- improvements to pytest's own test-suite leakage detection, courtesy of PRs + from Marc Abramowitz + 2.5.2 ----------------------------------- diff -r 61109199d97671367bf6498827b349afccb7a154 -r 8d04808c1e928179c11974741428a418a9378d37 testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -57,7 +57,7 @@ open_files = [t for t in lines2 if t[0] in new_fds] if open_files: error = [] - error.append("***** %s FD leackage detected" % len(open_files)) + error.append("***** %s FD leakage detected" % len(open_files)) error.extend([str(f) for f in open_files]) error.append("*** Before:") error.extend([str(f) for f in config._openfiles]) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 09:54:32 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 07:54:32 -0000 Subject: [Pytest-commit] commit/pytest: 4 new changesets Message-ID: <20140402075432.15574.2340@app18.ash-private.bitbucket.org> 4 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/da209ddea808/ Changeset: da209ddea808 Branch: nloadholtes/slight-change-to-the-wording-for-readabi-1395890493571 User: hpk42 Date: 2014-04-02 09:53:55 Summary: close branch Affected #: 0 files https://bitbucket.org/hpk42/pytest/commits/9acd460a73d3/ Changeset: 9acd460a73d3 Branch: graingert/pep8-good-practices-code-1394196858258 User: hpk42 Date: 2014-04-02 09:54:04 Summary: close branch Affected #: 0 files https://bitbucket.org/hpk42/pytest/commits/4ef67291d9fe/ Changeset: 4ef67291d9fe Branch: capsimple1 User: hpk42 Date: 2014-04-02 09:54:11 Summary: close branch Affected #: 0 files https://bitbucket.org/hpk42/pytest/commits/47494d3540ba/ Changeset: 47494d3540ba Branch: contributiondocs User: hpk42 Date: 2014-04-02 09:54:16 Summary: close branch Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Wed Apr 2 11:21:17 2014 From: issues-reply at bitbucket.org (Marc Abramowitz) Date: Wed, 02 Apr 2014 09:21:17 -0000 Subject: [Pytest-commit] Issue #496: Update plugin list (hpk42/pytest) Message-ID: <20140402092117.25740.7617@app04.ash-private.bitbucket.org> New issue 496: Update plugin list https://bitbucket.org/hpk42/pytest/issue/496/update-plugin-list Marc Abramowitz: Sorry to ADD a bug on pytest bug day while you're trying REDUCE the bug count, but I think that this should be a quick one http://pytest.org/latest/plugins_index/index.html seems to be out of date. The format there is completely different from the format in the hg tip (e.g.: it has version numbers for plugins, no icons for repos, etc.). It shows pytest-sugar-0.2.2 (old version) and that it's not Python 3 compatible, which is out of date. From commits-noreply at bitbucket.org Wed Apr 2 11:45:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 09:45:47 -0000 Subject: [Pytest-commit] commit/tox: marix: Update jenkins specific documentation to use proper section name. Message-ID: <20140402094547.20275.68801@app15.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/0c390972b576/ Changeset: 0c390972b576 User: marix Date: 2014-04-01 07:18:43 Summary: Update jenkins specific documentation to use proper section name. The configuration documentation still refered to tox:hudson instead of tox:jenkins. fixes #136 Affected #: 1 file diff -r da5a5817e35ddb4b63219b6194f4f215593525fd -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b doc/config.txt --- a/doc/config.txt +++ b/doc/config.txt @@ -28,9 +28,9 @@ (by checking for existence of the ``JENKINS_URL`` environment variable) and will first lookup global tox settings in this section:: - [tox:hudson] - ... # override [tox] settings for the hudson context - # note: for hudson distshare defaults to ``{toxworkdir}/distshare``. + [tox:jenkins] + ... # override [tox] settings for the jenkins context + # note: for jenkins distshare defaults to ``{toxworkdir}/distshare``. envlist setting Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 11:52:54 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 09:52:54 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue436: improved finding of initial conftest files from command Message-ID: <20140402095254.18770.19643@app05.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/083d8d0b065f/ Changeset: 083d8d0b065f User: hpk42 Date: 2014-04-02 11:29:23 Summary: fix issue436: improved finding of initial conftest files from command line arguments by using the result of parse_known_args rather than the previous flaky heuristics. Thanks Marc Abramowitz for tests and initial fixing approaches in this area. Affected #: 5 files diff -r 8d04808c1e928179c11974741428a418a9378d37 -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,11 @@ - change skips into warnings for test classes with an __init__ and callables in test modules which look like a test but are not functions. +- fix issue436: improved finding of initial conftest files from command + line arguments by using the result of parse_known_args rather than + the previous flaky heuristics. Thanks Marc Abramowitz for tests + and initial fixing approaches in this area. + - fix issue #479: properly handle nose/unittest(2) SkipTest exceptions during collection/loading of test modules. Thanks to Marc Schlaich for the complete PR. diff -r 8d04808c1e928179c11974741428a418a9378d37 -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -31,7 +31,7 @@ @pytest.mark.tryfirst def pytest_load_initial_conftests(early_config, parser, args, __multicall__): - ns = parser.parse_known_args(args) + ns = early_config.known_args_namespace pluginmanager = early_config.pluginmanager if ns.capture == "no": return diff -r 8d04808c1e928179c11974741428a418a9378d37 -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -449,38 +449,27 @@ """ the single place for accessing values and interacting towards conftest modules from pytest objects. """ - def __init__(self, onimport=None, confcutdir=None): + def __init__(self, onimport=None): self._path2confmods = {} self._onimport = onimport self._conftestpath2mod = {} - self._confcutdir = confcutdir + self._confcutdir = None - def setinitial(self, args): - """ try to find a first anchor path for looking up global values - from conftests. This function is usually called _before_ - argument parsing. conftest files may add command line options - and we thus have no completely safe way of determining - which parts of the arguments are actually related to options - and which are file system paths. We just try here to get - bootstrapped ... + def setinitial(self, namespace): + """ load initial conftest files given a preparsed "namespace". + As conftest files may add their own command line options + which have arguments ('--my-opt somepath') we might get some + false positives. All builtin and 3rd party plugins will have + been loaded, however, so common options will not confuse our logic + here. """ current = py.path.local() - opt = '--confcutdir' - for i in range(len(args)): - opt1 = str(args[i]) - if opt1.startswith(opt): - if opt1 == opt: - if len(args) > i: - p = current.join(args[i+1], abs=True) - elif opt1.startswith(opt + "="): - p = current.join(opt1[len(opt)+1:], abs=1) - self._confcutdir = p - break + self._confcutdir = current.join(namespace.confcutdir, abs=True) \ + if namespace.confcutdir else None + testpaths = namespace.file_or_dir foundanchor = False - for arg in args: - if hasattr(arg, 'startswith') and arg.startswith("--"): - continue - anchor = current.join(arg, abs=1) + for path in testpaths: + anchor = current.join(path, abs=1) if exists(anchor): # we found some file object self._try_load_conftest(anchor) foundanchor = True @@ -676,8 +665,8 @@ plugins += self._conftest.getconftestmodules(fspath) return plugins - def pytest_load_initial_conftests(self, parser, args): - self._conftest.setinitial(args) + def pytest_load_initial_conftests(self, early_config): + self._conftest.setinitial(early_config.known_args_namespace) pytest_load_initial_conftests.trylast = True def _initini(self, args): @@ -693,6 +682,7 @@ self.pluginmanager.consider_preparse(args) self.pluginmanager.consider_setuptools_entrypoints() self.pluginmanager.consider_env() + self.known_args_namespace = self._parser.parse_known_args(args) self.hook.pytest_load_initial_conftests(early_config=self, args=args, parser=self._parser) @@ -710,7 +700,6 @@ def parse(self, args): # parse given cmdline arguments into this config object. - # Note that this can only be called once per testing process. assert not hasattr(self, 'args'), ( "can only parse cmdline args at most once per Config object") self._origargs = args diff -r 8d04808c1e928179c11974741428a418a9378d37 -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 testing/test_config.py --- a/testing/test_config.py +++ b/testing/test_config.py @@ -148,7 +148,7 @@ assert config.getvalue('x') == 1 config.option.x = 2 assert config.getvalue('x') == 2 - config = testdir.parseconfig([str(o)]) + config = testdir.parseconfig(str(o)) assert config.getvalue('x') == 1 def test_getconftest_pathlist(self, testdir, tmpdir): diff -r 8d04808c1e928179c11974741428a418a9378d37 -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 testing/test_conftest.py --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -20,19 +20,26 @@ def ConftestWithSetinitial(path): conftest = Conftest() - conftest.setinitial([path]) + conftest_setinitial(conftest, [path]) return conftest +def conftest_setinitial(conftest, args, confcutdir=None): + class Namespace: + def __init__(self): + self.file_or_dir = args + self.confcutdir = str(confcutdir) + conftest.setinitial(Namespace()) + class TestConftestValueAccessGlobal: def test_basic_init(self, basedir): conftest = Conftest() - conftest.setinitial([basedir.join("adir")]) + conftest_setinitial(conftest, [basedir.join("adir")]) assert conftest.rget("a") == 1 def test_onimport(self, basedir): l = [] conftest = Conftest(onimport=l.append) - conftest.setinitial([basedir.join("adir"), + conftest_setinitial(conftest, [basedir.join("adir"), '--confcutdir=%s' % basedir]) assert len(l) == 1 assert conftest.rget("a") == 1 @@ -99,13 +106,13 @@ tmpdir.ensure("adir-1.0/__init__.py") ConftestWithSetinitial(tmpdir.join("adir-1.0", "b")) -def test_doubledash_not_considered(testdir): +def test_doubledash_considered(testdir): conf = testdir.mkdir("--option") conf.join("conftest.py").ensure() conftest = Conftest() - conftest.setinitial([conf.basename, conf.basename]) + conftest_setinitial(conftest, [conf.basename, conf.basename]) l = conftest.getconftestmodules(None) - assert len(l) == 0 + assert len(l) == 1 def test_issue151_load_all_conftests(testdir): names = "code proj src".split() @@ -114,7 +121,7 @@ p.ensure("conftest.py") conftest = Conftest() - conftest.setinitial(names) + conftest_setinitial(conftest, names) d = list(conftest._conftestpath2mod.values()) assert len(d) == len(names) @@ -142,8 +149,8 @@ def test_conftestcutdir(testdir): conf = testdir.makeconftest("") p = testdir.mkdir("x") - conftest = Conftest(confcutdir=p) - conftest.setinitial([testdir.tmpdir]) + conftest = Conftest() + conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p) l = conftest.getconftestmodules(p) assert len(l) == 0 l = conftest.getconftestmodules(conf.dirpath()) @@ -160,34 +167,18 @@ def test_conftestcutdir_inplace_considered(testdir): conf = testdir.makeconftest("") - conftest = Conftest(confcutdir=conf.dirpath()) - conftest.setinitial([conf.dirpath()]) + conftest = Conftest() + conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath()) l = conftest.getconftestmodules(conf.dirpath()) assert len(l) == 1 assert l[0].__file__.startswith(str(conf)) -def test_setinitial_confcut(testdir): - conf = testdir.makeconftest("") - sub = testdir.mkdir("sub") - sub.chdir() - for opts in (["--confcutdir=%s" % sub, sub], - [sub, "--confcutdir=%s" % sub], - ["--confcutdir=.", sub], - [sub, "--confcutdir", sub], - [str(sub), "--confcutdir", "."], - ): - conftest = Conftest() - conftest.setinitial(opts) - assert conftest._confcutdir == sub - assert conftest.getconftestmodules(sub) == [] - assert conftest.getconftestmodules(conf.dirpath()) == [] - @pytest.mark.parametrize("name", 'test tests whatever .dotdir'.split()) def test_setinitial_conftest_subdirs(testdir, name): sub = testdir.mkdir(name) subconftest = sub.ensure("conftest.py") conftest = Conftest() - conftest.setinitial([sub.dirpath(), '--confcutdir=%s' % testdir.tmpdir]) + conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir) if name not in ('whatever', '.dotdir'): assert subconftest in conftest._conftestpath2mod assert len(conftest._conftestpath2mod) == 1 @@ -205,6 +196,26 @@ result = testdir.runpytest("-h", "--confcutdir=%s" % x, x) result.stdout.fnmatch_lines(["*--xyz*"]) +def test_conftest_existing_resultlog(testdir): + x = testdir.mkdir("tests") + x.join("conftest.py").write(py.code.Source(""" + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true") + """)) + testdir.makefile(ext=".log", result="") # Writes result.log + result = testdir.runpytest("-h", "--resultlog", "result.log") + result.stdout.fnmatch_lines(["*--xyz*"]) + +def test_conftest_existing_junitxml(testdir): + x = testdir.mkdir("tests") + x.join("conftest.py").write(py.code.Source(""" + def pytest_addoption(parser): + parser.addoption("--xyz", action="store_true") + """)) + testdir.makefile(ext=".xml", junit="") # Writes junit.xml + result = testdir.runpytest("-h", "--junitxml", "junit.xml") + result.stdout.fnmatch_lines(["*--xyz*"]) + def test_conftest_import_order(testdir, monkeypatch): ct1 = testdir.makeconftest("") sub = testdir.mkdir("sub") Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 12:11:08 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 10:11:08 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue496: add pytest-sugar github repo override and regen index pages, upload it. Message-ID: <20140402101108.28831.79338@app02.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/195064115693/ Changeset: 195064115693 User: hpk42 Date: 2014-04-02 12:03:08 Summary: fix issue496: add pytest-sugar github repo override and regen index pages, upload it. Affected #: 2 files diff -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 -r 19506411569322fe9d014c4563ffec867f3073c9 doc/en/plugins_index/index.txt --- a/doc/en/plugins_index/index.txt +++ b/doc/en/plugins_index/index.txt @@ -1,127 +1,133 @@ -.. _plugins_index: - -List of Third-Party Plugins -=========================== - -The table below contains a listing of plugins found in PyPI and -their status when tested using py.test **2.5.2** and python 2.7 and -3.3. - -A complete listing can also be found at -`pytest-plugs `_, which contains tests -status against other py.test releases. - - -==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= - Name Py27 Py33 Repo Summary -==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= - `pytest-bdd `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2 .. image:: github.png BDD for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd - `pytest-bdd-splinter `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 .. image:: github.png Splinter subplugin for Pytest BDD plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd-splinter - `pytest-bench `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2 .. image:: github.png Benchmark utility that plugs into pytest. - :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2 :target: http://github.com/concordusapps/pytest-bench - `pytest-blockage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2 .. image:: github.png Disable network requests during a test run. - :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2 :target: https://github.com/rob-b/pytest-blockage - `pytest-browsermob-proxy `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 .. image:: github.png BrowserMob proxy plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-browsermob-proxy - `pytest-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/nibrahim/pytest_bugzilla - `pytest-cache `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin with mechanisms for caching across test runs - :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-cache/ - `pytest-capturelog `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to capture log messages - :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-capturelog/overview - `pytest-codecheckers `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to add source code sanity checks (pep8 and friends) - :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ - `pytest-contextfixture `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2 .. image:: github.png Define pytest fixtures as context managers. - :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-contextfixture/ - `pytest-couchdbkit `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test extension for per-test couchdb databases using couchdbkit - :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit - `pytest-cov `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing - :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-cov/overview - `pytest-dbfixtures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 .. image:: github.png dbfixtures plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 :target: https://github.com/clearcode/pytest-dbfixtures - `pytest-dbus-notification `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 .. image:: github.png D-BUS notifications for pytest results. - :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 :target: https://github.com/bmathieu33/pytest-dbus-notification - `pytest-django `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2 `link `_ A Django plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2 - `pytest-django-lite `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2 .. image:: github.png The bare minimum to integrate py.test with Django. - :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2 :target: https://github.com/dcramer/pytest-django-lite - `pytest-figleaf `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test figleaf coverage plugin - :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-figleaf - `pytest-flakes `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to check source code with pyflakes - :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2 :target: https://github.com/fschulze/pytest-flakes - `pytest-greendots `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2 ? Green progress dots - :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2 - `pytest-growl `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2 ? Growl notifications for pytest results. - :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2 - `pytest-httpretty `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2 .. image:: github.png A thin wrapper of HTTPretty for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2 :target: http://github.com/papaeye/pytest-httpretty - `pytest-incremental `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png an incremental test runner (pytest plugin) - :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/schettino72/pytest-incremental - `pytest-instafail `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to show failures instantly - :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2 :target: https://github.com/jpvanhal/pytest-instafail - `pytest-ipdb `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2 .. image:: github.png A py.test plug-in to enable drop to ipdb debugger on test failure. - :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2 :target: https://github.com/mverteuil/pytest-ipdb - `pytest-jira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test JIRA integration plugin, using markers - :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2 :target: http://github.com/jlaska/pytest_jira - `pytest-konira `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2 .. image:: github.png Run Konira DSL tests with py.test - :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2 :target: http://github.com/alfredodeza/pytest-konira - `pytest-localserver `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to test server connections locally. - :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-localserver/ - `pytest-marker-bugzilla `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin, using markers - :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/eanxgeek/pytest_marker_bugzilla - `pytest-markfiltration `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-markfiltration - `pytest-marks `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-marks - `pytest-monkeyplus `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest's monkeypatch subclass with extra functionalities - :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/ - `pytest-mozwebqa `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 .. image:: github.png Mozilla WebQA plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-mozwebqa - `pytest-oerp `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to test OpenERP modules - :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-oerp/ - `pytest-osxnotify `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2 .. image:: github.png OS X notifications for py.test results. - :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2 :target: https://github.com/dbader/pytest-osxnotify - `pytest-paste-config `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2 ? Allow setting the path to a paste config file - :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2 - `pytest-pep8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to check PEP8 requirements - :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-pep8/ - `pytest-poo `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2 .. image:: github.png Visualize your crappy tests - :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-poo - `pytest-pydev `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to connect to a remote debug server with PyDev or PyCharm. - :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-pydev/ - `pytest-pythonpath `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin for adding to the PYTHONPATH from command line or configs. - :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2 :target: https://github.com/bigsassy/pytest-pythonpath - `pytest-qt `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications. - :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2 :target: http://github.com/nicoddemus/pytest-qt - `pytest-quickcheck `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to generate random data inspired by QuickCheck - :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/t2y/pytest-quickcheck/ - `pytest-rage `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to implement PEP712 - :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-rage/ - `pytest-random `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to randomize tests - :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-random - `pytest-rerunfailures `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to re-run tests to eliminate flakey failures - :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-rerunfailures - `pytest-runfailed `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2 .. image:: github.png implement a --failed option for pytest - :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed - `pytest-runner `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN - :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner - `pytest-sugar `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: github.png changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). - :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 :target: https://github.com/Frozenball/pytest-sugar - `pytest-timeout `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout - :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ - `pytest-twisted `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 .. image:: github.png A twisted plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2 :target: https://github.com/schmir/pytest-twisted - `pytest-xdist `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test xdist plugin for distributed testing and loop-on-failing modes - :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xdist - `pytest-xprocess `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to manage external processes across test runs - :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xprocess/ - `pytest-yamlwsgi `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 ? Run tests against wsgi apps defined in yaml - :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 - `pytest-zap `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2 .. image:: github.png OWASP ZAP plugin for py.test. - :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-zap - -==================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= - -*(Updated on 2014-02-18)* +.. _plugins_index: + +List of Third-Party Plugins +=========================== + +The table below contains a listing of plugins found in PyPI and +their status when tested using py.test **2.5.2** and python 2.7 and +3.3. + +A complete listing can also be found at +`pytest-plugs `_, which contains tests +status against other py.test releases. + + +========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + Name Py27 Py33 Repo Summary +========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + `pytest-bdd-2.0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-latest?py=py33&pytest=2.5.2 .. image:: github.png BDD for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd + `pytest-bdd-splinter-0.5.99 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 .. image:: github.png Splinter subplugin for Pytest BDD plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bdd-splinter-latest?py=py33&pytest=2.5.2 :target: https://github.com/olegpidsadnyi/pytest-bdd-splinter + `pytest-bench-0.2.5 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bench-latest?py=py33&pytest=2.5.2 .. image:: github.png Benchmark utility that plugs into pytest. + :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bench-latest?py=py33&pytest=2.5.2 :target: http://github.com/concordusapps/pytest-bench + `pytest-blockage-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-blockage-latest?py=py33&pytest=2.5.2 .. image:: github.png Disable network requests during a test run. + :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-blockage-latest?py=py33&pytest=2.5.2 :target: https://github.com/rob-b/pytest-blockage + `pytest-browsermob-proxy-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 .. image:: github.png BrowserMob proxy plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-browsermob-proxy-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-browsermob-proxy + `pytest-bugzilla-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/nibrahim/pytest_bugzilla + `pytest-cache-1.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cache-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin with mechanisms for caching across test runs + :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cache-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-cache/ + `pytest-capturelog-0.7 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-capturelog-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to capture log messages + :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-capturelog-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-capturelog/overview + `pytest-codecheckers-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-codecheckers-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to add source code sanity checks (pep8 and friends) + :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-codecheckers-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-codecheckers/ + `pytest-contextfixture-0.1.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-contextfixture-latest?py=py33&pytest=2.5.2 .. image:: github.png Define pytest fixtures as context managers. + :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-contextfixture-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-contextfixture/ + `pytest-couchdbkit-0.5.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test extension for per-test couchdb databases using couchdbkit + :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-couchdbkit-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/RonnyPfannschmidt/pytest-couchdbkit + `pytest-cov-1.6 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-cov-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin for coverage reporting with support for both centralised and distributed testing, including subprocesses and multiprocessing + :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-cov-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/memedough/pytest-cov/overview + `pytest-dbfixtures-0.4.6 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 .. image:: github.png Databases fixtures plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbfixtures-latest?py=py33&pytest=2.5.2 :target: https://github.com/clearcode/pytest-dbfixtures + `pytest-dbus-notification-1.0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 .. image:: github.png D-BUS notifications for pytest results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-dbus-notification-latest?py=py33&pytest=2.5.2 :target: https://github.com/bmathieu33/pytest-dbus-notification + `pytest-diffeo-0.1.4 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-diffeo-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-diffeo-latest?py=py33&pytest=2.5.2 .. image:: github.png Common py.test support for Diffeo packages + :target: http://pytest-plugs.herokuapp.com/output/pytest-diffeo-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-diffeo-latest?py=py33&pytest=2.5.2 :target: https://github.com/diffeo/pytest-diffeo + `pytest-django-2.6 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-latest?py=py33&pytest=2.5.2 `link `_ A Django plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-latest?py=py33&pytest=2.5.2 + `pytest-django-haystack-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-haystack-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-haystack-latest?py=py33&pytest=2.5.2 .. image:: github.png Cleanup your Haystack indexes between tests + :target: http://pytest-plugs.herokuapp.com/output/pytest-django-haystack-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-haystack-latest?py=py33&pytest=2.5.2 :target: http://github.com/rouge8/pytest-django-haystack + `pytest-django-lite-0.1.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-django-lite-latest?py=py33&pytest=2.5.2 .. image:: github.png The bare minimum to integrate py.test with Django. + :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-django-lite-latest?py=py33&pytest=2.5.2 :target: https://github.com/dcramer/pytest-django-lite + `pytest-figleaf-1.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-figleaf-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test figleaf coverage plugin + :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-figleaf-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-figleaf + `pytest-flakes-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-flakes-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to check source code with pyflakes + :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-flakes-latest?py=py33&pytest=2.5.2 :target: https://github.com/fschulze/pytest-flakes + `pytest-greendots-0.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-greendots-latest?py=py33&pytest=2.5.2 ? Green progress dots + :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-greendots-latest?py=py33&pytest=2.5.2 + `pytest-growl-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-growl-latest?py=py33&pytest=2.5.2 ? Growl notifications for pytest results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-growl-latest?py=py33&pytest=2.5.2 + `pytest-httpretty-0.2.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-httpretty-latest?py=py33&pytest=2.5.2 .. image:: github.png A thin wrapper of HTTPretty for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-httpretty-latest?py=py33&pytest=2.5.2 :target: http://github.com/papaeye/pytest-httpretty + `pytest-incremental-0.3.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-incremental-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png an incremental test runner (pytest plugin) + :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-incremental-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/schettino72/pytest-incremental + `pytest-instafail-0.2.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-instafail-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to show failures instantly + :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-instafail-latest?py=py33&pytest=2.5.2 :target: https://github.com/jpvanhal/pytest-instafail + `pytest-ipdb-0.1-prerelease `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ipdb-latest?py=py33&pytest=2.5.2 .. image:: github.png A py.test plug-in to enable drop to ipdb debugger on test failure. + :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-ipdb-latest?py=py33&pytest=2.5.2 :target: https://github.com/mverteuil/pytest-ipdb + `pytest-jira-0.01 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-jira-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test JIRA integration plugin, using markers + :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-jira-latest?py=py33&pytest=2.5.2 :target: http://github.com/jlaska/pytest_jira + `pytest-konira-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-konira-latest?py=py33&pytest=2.5.2 .. image:: github.png Run Konira DSL tests with py.test + :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-konira-latest?py=py33&pytest=2.5.2 :target: http://github.com/alfredodeza/pytest-konira + `pytest-localserver-0.3.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-localserver-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to test server connections locally. + :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-localserver-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-localserver/ + `pytest-marker-bugzilla-0.06 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test bugzilla integration plugin, using markers + :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marker-bugzilla-latest?py=py33&pytest=2.5.2 :target: http://github.com/eanxgeek/pytest_marker_bugzilla + `pytest-markfiltration-0.8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-markfiltration-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-markfiltration-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-markfiltration + `pytest-marks-0.4 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-marks-latest?py=py33&pytest=2.5.2 .. image:: github.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-marks-latest?py=py33&pytest=2.5.2 :target: https://github.com/adamgoucher/pytest-marks + `pytest-monkeyplus-1.1.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest's monkeypatch subclass with extra functionalities + :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-monkeyplus-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hsoft/pytest-monkeyplus/ + `pytest-mozwebqa-1.1.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 .. image:: github.png Mozilla WebQA plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-mozwebqa-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-mozwebqa + `pytest-oerp-0.2.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-oerp-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to test OpenERP modules + :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-oerp-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-oerp/ + `pytest-ordering-0.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ordering-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-ordering-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to run your tests in a specific order + :target: http://pytest-plugs.herokuapp.com/output/pytest-ordering-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-ordering-latest?py=py33&pytest=2.5.2 :target: https://github.com/ftobia/pytest-ordering + `pytest-osxnotify-0.1.4 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-osxnotify-latest?py=py33&pytest=2.5.2 .. image:: github.png OS X notifications for py.test results. + :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-osxnotify-latest?py=py33&pytest=2.5.2 :target: https://github.com/dbader/pytest-osxnotify + `pytest-paste-config-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-paste-config-latest?py=py33&pytest=2.5.2 ? Allow setting the path to a paste config file + :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-paste-config-latest?py=py33&pytest=2.5.2 + `pytest-pep8-1.0.5 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pep8-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to check PEP8 requirements + :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pep8-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-pep8/ + `pytest-poo-0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-poo-latest?py=py33&pytest=2.5.2 .. image:: github.png Visualize your crappy tests + :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-poo-latest?py=py33&pytest=2.5.2 :target: http://github.com/pelme/pytest-poo + `pytest-pydev-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pydev-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test plugin to connect to a remote debug server with PyDev or PyCharm. + :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pydev-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/basti/pytest-pydev/ + `pytest-pythonpath-0.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-pythonpath-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin for adding to the PYTHONPATH from command line or configs. + :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-pythonpath-latest?py=py33&pytest=2.5.2 :target: https://github.com/bigsassy/pytest-pythonpath + `pytest-qt-1.0.2 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-qt-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin that adds fixtures for testing Qt (PyQt and PySide) applications. + :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-qt-latest?py=py33&pytest=2.5.2 :target: http://github.com/nicoddemus/pytest-qt + `pytest-quickcheck-0.8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-quickcheck-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to generate random data inspired by QuickCheck + :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-quickcheck-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/t2y/pytest-quickcheck/ + `pytest-rage-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rage-latest?py=py33&pytest=2.5.2 .. image:: github.png pytest plugin to implement PEP712 + :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rage-latest?py=py33&pytest=2.5.2 :target: http://github.com/santagada/pytest-rage/ + `pytest-random-0.02 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-random-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to randomize tests + :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-random-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-random + `pytest-rerunfailures-0.03 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test plugin to re-run tests to eliminate flakey failures + :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-rerunfailures-latest?py=py33&pytest=2.5.2 :target: https://github.com/klrmn/pytest-rerunfailures + `pytest-runfailed-0.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runfailed-latest?py=py33&pytest=2.5.2 .. image:: github.png implement a --failed option for pytest + :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runfailed-latest?py=py33&pytest=2.5.2 :target: http://github.com/dmerejkowsky/pytest-runfailed + `pytest-runner-2.0 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-runner-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png UNKNOWN + :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-runner-latest?py=py33&pytest=2.5.2 :target: https://bitbucket.org/jaraco/pytest-runner + `pytest-sugar-0.3.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-sugar-latest?py=py33&pytest=2.5.2 .. image:: github.png py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly). + :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-sugar-latest?py=py33&pytest=2.5.2 :target: https://github.com/Frozenball/pytest-sugar + `pytest-timeout-0.3 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-timeout-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to abort tests after a timeout + :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-timeout-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/flub/pytest-timeout/ + `pytest-twisted-1.5 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-twisted-latest?py=py33&pytest=2.5.2 .. image:: github.png A twisted plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-twisted-latest?py=py33&pytest=2.5.2 :target: https://github.com/schmir/pytest-twisted + `pytest-xdist-1.10 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xdist-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png py.test xdist plugin for distributed testing and loop-on-failing modes + :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xdist-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xdist + `pytest-xprocess-0.8 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-xprocess-latest?py=py33&pytest=2.5.2 .. image:: bitbucket.png pytest plugin to manage external processes across test runs + :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-xprocess-latest?py=py33&pytest=2.5.2 :target: http://bitbucket.org/hpk42/pytest-xprocess/ + `pytest-yamlwsgi-0.6 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 ? Run tests against wsgi apps defined in yaml + :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-yamlwsgi-latest?py=py33&pytest=2.5.2 + `pytest-zap-0.1 `_ .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py27&pytest=2.5.2 .. image:: http://pytest-plugs.herokuapp.com/status/pytest-zap-latest?py=py33&pytest=2.5.2 .. image:: github.png OWASP ZAP plugin for py.test. + :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py27&pytest=2.5.2 :target: http://pytest-plugs.herokuapp.com/output/pytest-zap-latest?py=py33&pytest=2.5.2 :target: https://github.com/davehunt/pytest-zap + +========================================================================================== ============================================================================================================ ============================================================================================================ ========================================================================= ============================================================================================================================================= + +*(Updated on 2014-04-02)* diff -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 -r 19506411569322fe9d014c4563ffec867f3073c9 doc/en/plugins_index/plugins_index.py --- a/doc/en/plugins_index/plugins_index.py +++ b/doc/en/plugins_index/plugins_index.py @@ -123,7 +123,7 @@ image_url = url.format(**common_params) image_url += '?py={py}&pytest={pytest}' row = ( - ColumnData(package_name, release_data['package_url']), + ColumnData(package_name + "-" + version, release_data['package_url']), ColumnData(image_url.format(py='py27', pytest=pytest_version), None), ColumnData(image_url.format(py='py33', pytest=pytest_version), @@ -170,6 +170,7 @@ return { 'pytest-blockage': 'https://github.com/rob-b/pytest-blockage', 'pytest-konira': 'http://github.com/alfredodeza/pytest-konira', + 'pytest-sugar': 'https://github.com/Frozenball/pytest-sugar', } Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 12:32:40 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 10:32:40 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140402103240.27511.20878@app17.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/e11d9acd5482/ Changeset: e11d9acd5482 User: hpk42 Date: 2014-04-02 12:32:21 Summary: fix issue492: avoid leak in test_writeorg Affected #: 2 files diff -r 19506411569322fe9d014c4563ffec867f3073c9 -r e11d9acd54826bd22d59337cbe50b5558d90cd38 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -40,6 +40,8 @@ - improvements to pytest's own test-suite leakage detection, courtesy of PRs from Marc Abramowitz +- fix issue492: avoid leak in test_writeorg + 2.5.2 ----------------------------------- diff -r 19506411569322fe9d014c4563ffec867f3073c9 -r e11d9acd54826bd22d59337cbe50b5558d90cd38 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -709,13 +709,11 @@ def test_writeorg(self, tmpfile): data1, data2 = tobytes("foo"), tobytes("bar") - try: - cap = capture.FDCapture(tmpfile.fileno()) - cap.start() - tmpfile.write(data1) - cap.writeorg(data2) - finally: - tmpfile.close() + cap = capture.FDCapture(tmpfile.fileno()) + cap.start() + tmpfile.write(data1) + tmpfile.flush() + cap.writeorg(data2) scap = cap.snap() cap.done() assert scap == totext(data1) https://bitbucket.org/hpk42/pytest/commits/565d840023be/ Changeset: 565d840023be User: hpk42 Date: 2014-04-02 12:32:30 Summary: fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz. Affected #: 1 file diff -r e11d9acd54826bd22d59337cbe50b5558d90cd38 -r 565d840023be5a4c59135be1a11b7bca2a97b663 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -40,7 +40,7 @@ - improvements to pytest's own test-suite leakage detection, courtesy of PRs from Marc Abramowitz -- fix issue492: avoid leak in test_writeorg +- fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz. 2.5.2 ----------------------------------- Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 12:48:43 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 10:48:43 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue493: don't run tests in doc directory with ``python setup.py test`` Message-ID: <20140402104843.901.66908@app02.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/e3309a39fb91/ Changeset: e3309a39fb91 User: hpk42 Date: 2014-04-02 12:48:35 Summary: fix issue493: don't run tests in doc directory with ``python setup.py test`` (use tox -e doctesting for that) Affected #: 3 files diff -r 565d840023be5a4c59135be1a11b7bca2a97b663 -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -42,6 +42,9 @@ - fix issue492: avoid leak in test_writeorg. Thanks Marc Abramowitz. +- fix issue493: don't run tests in doc directory with ``python setup.py test`` + (use tox -e doctesting for that) + 2.5.2 ----------------------------------- diff -r 565d840023be5a4c59135be1a11b7bca2a97b663 -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 setup.py --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ PPATH=[x for x in os.environ.get("PYTHONPATH", "").split(":") if x] PPATH.insert(0, os.getcwd()) os.environ["PYTHONPATH"] = ":".join(PPATH) - errno = subprocess.call([sys.executable, 'pytest.py']) + errno = subprocess.call([sys.executable, 'pytest.py', '--ignore=doc']) raise SystemExit(errno) if __name__ == '__main__': diff -r 565d840023be5a4c59135be1a11b7bca2a97b663 -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] distshare={homedir}/.tox/distshare -envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial +envlist=flakes,py26,py27,pypy,py27-pexpect,py33-pexpect,py27-nobyte,py32,py33,py27-xdist,py33-xdist,py27-trial,py33-trial,doctesting [testenv] changedir=testing @@ -92,6 +92,12 @@ make clean make html +[testenv:doctesting] +basepython=python3.3 +changedir=doc/en +deps=PyYAML +commands= py.test -rfsxX --junitxml={envlogdir}/junit-{envname}.xml [] + [testenv:regen] basepython=python changedir=doc/en Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 18:17:06 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 16:17:06 -0000 Subject: [Pytest-commit] commit/pytest: flub: Style fixes for pep8 Message-ID: <20140402161706.7002.52755@app11.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/66efe3231c7b/ Changeset: 66efe3231c7b User: flub Date: 2014-04-02 18:16:37 Summary: Style fixes for pep8 Includes a quotation change for consistent style. Affected #: 1 file diff -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 -r 66efe3231c7bdb6316d9e0fcd9f76f36d1063d06 _pytest/assertion/__init__.py --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -6,20 +6,32 @@ from _pytest.monkeypatch import monkeypatch from _pytest.assertion import util + def pytest_addoption(parser): group = parser.getgroup("debugconfig") - group.addoption('--assert', action="store", dest="assertmode", + group.addoption('--assert', + action="store", + dest="assertmode", choices=("rewrite", "reinterp", "plain",), - default="rewrite", metavar="MODE", - help="""control assertion debugging tools. -'plain' performs no assertion debugging. -'reinterp' reinterprets assert statements after they failed to provide assertion expression information. -'rewrite' (the default) rewrites assert statements in test modules on import -to provide assert expression information. """) - group.addoption('--no-assert', action="store_true", default=False, - dest="noassert", help="DEPRECATED equivalent to --assert=plain") - group.addoption('--nomagic', '--no-magic', action="store_true", - default=False, help="DEPRECATED equivalent to --assert=plain") + default="rewrite", + metavar="MODE", + help="""control assertion debugging tools. 'plain' + performs no assertion debugging. 'reinterp' + reinterprets assert statements after they failed + to provide assertion expression information. + 'rewrite' (the default) rewrites assert + statements in test modules on import to + provide assert expression information. """) + group.addoption('--no-assert', + action="store_true", + default=False, + dest="noassert", + help="DEPRECATED equivalent to --assert=plain") + group.addoption('--nomagic', '--no-magic', + action="store_true", + default=False, + help="DEPRECATED equivalent to --assert=plain") + class AssertionState: """State for the assertion plugin.""" @@ -28,6 +40,7 @@ self.mode = mode self.trace = config.trace.root.get("assertion") + def pytest_configure(config): mode = config.getvalue("assertmode") if config.getvalue("noassert") or config.getvalue("nomagic"): @@ -41,7 +54,7 @@ # Both Jython and CPython 2.6.0 have AST bugs that make the # assertion rewriting hook malfunction. if (sys.platform.startswith('java') or - sys.version_info[:3] == (2, 6, 0)): + sys.version_info[:3] == (2, 6, 0)): mode = "reinterp" if mode != "plain": _load_modules(mode) @@ -58,11 +71,13 @@ config._assertstate.hook = hook config._assertstate.trace("configured with mode set to %r" % (mode,)) + def pytest_unconfigure(config): hook = config._assertstate.hook if hook is not None and hook in sys.meta_path: sys.meta_path.remove(hook) + def pytest_collection(session): # this hook is only called when test modules are collected # so for example not in the master process of pytest-xdist @@ -71,11 +86,11 @@ if hook is not None: hook.set_session(session) + def pytest_runtest_setup(item): def callbinrepr(op, left, right): hook_result = item.ihook.pytest_assertrepr_compare( config=item.config, op=op, left=left, right=right) - for new_expl in hook_result: if new_expl: # Don't include pageloads of data unless we are very @@ -84,7 +99,7 @@ and item.config.option.verbose < 2): new_expl[1:] = [py.builtin._totext( 'Detailed information truncated, use "-vv" to show')] - res = py.builtin._totext('\n~').join(new_expl) + res = py.builtin._totext("\n~").join(new_expl) if item.config.getvalue("assertmode") == "rewrite": # The result will be fed back a python % formatting # operation, which will fail if there are extraneous @@ -93,14 +108,17 @@ return res util._reprcompare = callbinrepr + def pytest_runtest_teardown(item): util._reprcompare = None + def pytest_sessionfinish(session): hook = session.config._assertstate.hook if hook is not None: hook.session = None + def _load_modules(mode): """Lazily import assertion related code.""" global rewrite, reinterpret @@ -108,6 +126,7 @@ if mode == "rewrite": from _pytest.assertion import rewrite # noqa + def warn_about_missing_assertion(mode): try: assert False @@ -121,8 +140,9 @@ specifically = "failing tests may report as passing" sys.stderr.write("WARNING: " + specifically + - " because assert statements are not executed " - "by the underlying Python interpreter " - "(are you using python -O?)\n") + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n") + pytest_assertrepr_compare = util.assertrepr_compare Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 18:55:35 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 16:55:35 -0000 Subject: [Pytest-commit] commit/pytest: flub: Escape newlines in result from assertrepr hook Message-ID: <20140402165535.26285.54046@app17.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/a1f920be1e33/ Changeset: a1f920be1e33 User: flub Date: 2014-04-02 18:35:22 Summary: Escape newlines in result from assertrepr hook The result from the pytest_assertrepr_compare hook should not include any newlines since that will confuse the mini-formatting language used by assertion.util.format_explanation. So simply escape the included newlines, this way hook writers do not have to worry about this at all. Fixes issue 453. Affected #: 2 files diff -r 66efe3231c7bdb6316d9e0fcd9f76f36d1063d06 -r a1f920be1e33b3ad8c2f0549d482119b52d7f4e1 _pytest/assertion/__init__.py --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -88,22 +88,38 @@ def pytest_runtest_setup(item): + """Setup the pytest_assertrepr_compare hook + + The newinterpret and rewrite modules will use util._reprcompare if + it exists to use custom reporting via the + pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ def callbinrepr(op, left, right): + """Call the pytest_assertrepr_compare hook and prepare the result + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are dropped unles -vv was used. + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ hook_result = item.ihook.pytest_assertrepr_compare( config=item.config, op=op, left=left, right=right) for new_expl in hook_result: if new_expl: - # Don't include pageloads of data unless we are very - # verbose (-vv) if (sum(len(p) for p in new_expl[1:]) > 80*8 and item.config.option.verbose < 2): new_expl[1:] = [py.builtin._totext( 'Detailed information truncated, use "-vv" to show')] + new_expl = [line.replace("\n", "\\n") for line in new_expl] res = py.builtin._totext("\n~").join(new_expl) if item.config.getvalue("assertmode") == "rewrite": - # The result will be fed back a python % formatting - # operation, which will fail if there are extraneous - # '%'s in the string. Escape them here. res = res.replace("%", "%%") return res util._reprcompare = callbinrepr @@ -145,4 +161,5 @@ "(are you using python -O?)\n") +# Expose this plugin's implementation for the pytest_assertrepr_compare hook pytest_assertrepr_compare = util.assertrepr_compare diff -r 66efe3231c7bdb6316d9e0fcd9f76f36d1063d06 -r a1f920be1e33b3ad8c2f0549d482119b52d7f4e1 testing/test_assertion.py --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -4,6 +4,7 @@ import py, pytest import _pytest.assertion as plugin from _pytest.assertion import reinterpret +from _pytest.assertion import util needsnewassert = pytest.mark.skipif("sys.version_info < (2,6)") @@ -199,6 +200,21 @@ assert msg +class TestFormatExplanation: + + def test_speical_chars_full(self, testdir): + # Issue 453, for the bug this would raise IndexError + testdir.makepyfile(""" + def test_foo(): + assert u'\\n}' == u'' + """) + result = testdir.runpytest() + assert result.ret == 1 + result.stdout.fnmatch_lines([ + "*AssertionError*", + ]) + + def test_python25_compile_issue257(testdir): testdir.makepyfile(""" def test_rewritten(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 18:56:50 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 16:56:50 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140402165650.25588.17696@app10.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/4fb68209f94b/ Changeset: 4fb68209f94b Branch: remove_unused_import User: Marc Abramowitz Date: 2014-04-02 18:14:16 Summary: testing/test_capture.py: Remove unused `import tempfile` Affected #: 1 file diff -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 -r 4fb68209f94b2743ee45de1327166c6a1ced6352 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -4,7 +4,6 @@ import os import sys import py -import tempfile import pytest import contextlib https://bitbucket.org/hpk42/pytest/commits/78f58544767e/ Changeset: 78f58544767e Branch: remove_unused_import User: Marc Abramowitz Date: 2014-04-02 18:48:08 Summary: test_capture.py: More PEP8. Remove unused `cap` in `with`. Affected #: 1 file diff -r 4fb68209f94b2743ee45de1327166c6a1ced6352 -r 78f58544767e2c23209c81cece9e08f7c7e0702d testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -857,7 +857,7 @@ def test_stdin_restored(self): old = sys.stdin - with self.getcapture(in_=True) as cap: + with self.getcapture(in_=True): newstdin = sys.stdin assert newstdin != sys.stdin assert sys.stdin is old @@ -866,7 +866,7 @@ print ("XXX this test may well hang instead of crashing") print ("XXX which indicates an error in the underlying capturing") print ("XXX mechanisms") - with self.getcapture() as cap: + with self.getcapture(): pytest.raises(IOError, "sys.stdin.read()") https://bitbucket.org/hpk42/pytest/commits/fad24b804a2f/ Changeset: fad24b804a2f User: hpk42 Date: 2014-04-02 18:56:46 Summary: Merged in msabramo/pytest/remove_unused_import (pull request #146) testing/test_capture.py: Remove unused stuff for PEP8 compliance Affected #: 1 file diff -r a1f920be1e33b3ad8c2f0549d482119b52d7f4e1 -r fad24b804a2f20f4f7536f4bd6e6c3ab8c55322f testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -4,7 +4,6 @@ import os import sys import py -import tempfile import pytest import contextlib @@ -858,7 +857,7 @@ def test_stdin_restored(self): old = sys.stdin - with self.getcapture(in_=True) as cap: + with self.getcapture(in_=True): newstdin = sys.stdin assert newstdin != sys.stdin assert sys.stdin is old @@ -867,7 +866,7 @@ print ("XXX this test may well hang instead of crashing") print ("XXX which indicates an error in the underlying capturing") print ("XXX mechanisms") - with self.getcapture() as cap: + with self.getcapture(): pytest.raises(IOError, "sys.stdin.read()") Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 19:00:36 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 17:00:36 -0000 Subject: [Pytest-commit] commit/pytest: flub: Mention fix for issue 453 in changelog Message-ID: <20140402170036.24354.94414@app12.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/6979ff35cc61/ Changeset: 6979ff35cc61 User: flub Date: 2014-04-02 19:00:25 Summary: Mention fix for issue 453 in changelog Affected #: 1 file diff -r fad24b804a2f20f4f7536f4bd6e6c3ab8c55322f -r 6979ff35cc6114c06e3aa6c86c0b6d700a6a6390 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue453: the result of the pytest_assertrepr_compare hook now gets + it's newlines escaped so that format_exception does not blow up. + - internal new warning system: pytest will now produce warnings when it detects oddities in your test collection or execution. Warnings are ultimately sent to a new pytest_logwarning hook which is Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 19:13:31 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 17:13:31 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140402171331.26466.13054@app08.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/e9d688fab765/ Changeset: e9d688fab765 Branch: add_drone_io_to_README_rst User: Marc Abramowitz Date: 2014-04-02 12:11:35 Summary: README.rst: Add drone.io link and build badge image Affected #: 1 file diff -r 083d8d0b065f11860fc1ec4edb50480b6ee50b35 -r e9d688fab7658a2cad6c8e7e89818f009a6c5657 README.rst --- a/README.rst +++ b/README.rst @@ -5,6 +5,11 @@ Issues: https://bitbucket.org/hpk42/pytest/issues?status=open +CI: https://drone.io/bitbucket.org/hpk42/pytest + +.. image:: https://drone.io/bitbucket.org/hpk42/pytest/status.png + :target: https://drone.io/bitbucket.org/hpk42/pytest + The ``pytest`` testing tool makes it easy to write small tests, yet scales to support complex functional testing. It provides https://bitbucket.org/hpk42/pytest/commits/9b5b4b35eb48/ Changeset: 9b5b4b35eb48 Branch: add_drone_io_to_README_rst User: Marc Abramowitz Date: 2014-04-02 15:41:33 Summary: README.rst: Remove drone.io build badge; keep link hpk had concerns about the build badge confusing folks when shown on PyPI. Affected #: 1 file diff -r e9d688fab7658a2cad6c8e7e89818f009a6c5657 -r 9b5b4b35eb48ee5ca4a35d45d40597625e095db9 README.rst --- a/README.rst +++ b/README.rst @@ -7,9 +7,6 @@ CI: https://drone.io/bitbucket.org/hpk42/pytest -.. image:: https://drone.io/bitbucket.org/hpk42/pytest/status.png - :target: https://drone.io/bitbucket.org/hpk42/pytest - The ``pytest`` testing tool makes it easy to write small tests, yet scales to support complex functional testing. It provides https://bitbucket.org/hpk42/pytest/commits/443d4791a41f/ Changeset: 443d4791a41f User: hpk42 Date: 2014-04-02 19:13:26 Summary: Merged in msabramo/pytest/add_drone_io_to_README_rst (pull request #143) README.rst: Add drone.io link Affected #: 1 file diff -r 6979ff35cc6114c06e3aa6c86c0b6d700a6a6390 -r 443d4791a41f40bbac4e5355c6c4a395b9fb2864 README.rst --- a/README.rst +++ b/README.rst @@ -5,6 +5,8 @@ Issues: https://bitbucket.org/hpk42/pytest/issues?status=open +CI: https://drone.io/bitbucket.org/hpk42/pytest + The ``pytest`` testing tool makes it easy to write small tests, yet scales to support complex functional testing. It provides Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 20:30:15 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 18:30:15 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: add a test for robustness of capturing when a test closes FD1/2 Message-ID: <20140402183015.5813.89180@app15.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/abb19eb7123a/ Changeset: abb19eb7123a User: hpk42 Date: 2014-04-02 20:29:10 Summary: add a test for robustness of capturing when a test closes FD1/2 Affected #: 1 file diff -r 443d4791a41f40bbac4e5355c6c4a395b9fb2864 -r abb19eb7123acf5673798b34e47248807fc82019 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1010,3 +1010,21 @@ WARNING:root:hello2 """) assert "atexit" not in result.stderr.str() + +def test_close_and_capture_again(testdir): + testdir.makepyfile(""" + import os + def test_close(): + os.close(1) + def test_capture_again(): + os.write(1, "hello\\n") + assert 0 + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *test_capture_again* + *assert 0* + *stdout* + *hello* + """) + Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 2 20:50:06 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 18:50:06 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix test on py33, thanks msabramo1 Message-ID: <20140402185006.24701.77867@app03.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/ceafafc62657/ Changeset: ceafafc62657 User: hpk42 Date: 2014-04-02 20:49:57 Summary: fix test on py33, thanks msabramo1 Affected #: 1 file diff -r abb19eb7123acf5673798b34e47248807fc82019 -r ceafafc62657ed15b0486e8838f20b06abc0880d testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1017,7 +1017,7 @@ def test_close(): os.close(1) def test_capture_again(): - os.write(1, "hello\\n") + os.write(1, b"hello\\n") assert 0 """) result = testdir.runpytest() Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 00:19:09 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 02 Apr 2014 22:19:09 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140402221909.27839.77500@app07.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/202e6aee5890/ Changeset: 202e6aee5890 Branch: issue486 User: hpk42 Date: 2014-04-02 20:42:41 Summary: fix issue486: better reporting and handling of early conftest loading failures Affected #: 4 files diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 202e6aee58906bf7fc5313d8567bb0451497394f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -48,6 +48,9 @@ - fix issue493: don't run tests in doc directory with ``python setup.py test`` (use tox -e doctesting for that) +- fix issue486: better reporting and handling of early conftest loading failures + + 2.5.2 ----------------------------------- diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 202e6aee58906bf7fc5313d8567bb0451497394f _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -7,6 +7,13 @@ from _pytest.core import PluginManager # pytest startup +# +class ConftestImportFailure(Exception): + def __init__(self, path, excinfo): + Exception.__init__(self, path, excinfo) + self.path = path + self.excinfo = excinfo + def main(args=None, plugins=None): """ return exit code, after performing an in-process test run. @@ -16,8 +23,17 @@ :arg plugins: list of plugin objects to be auto-registered during initialization. """ - config = _prepareconfig(args, plugins) - return config.hook.pytest_cmdline_main(config=config) + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure: + e = sys.exc_info()[1] + tw = py.io.TerminalWriter(sys.stderr) + for line in py.std.traceback.format_exception(*e.excinfo): + tw.line(line.rstrip(), red=True) + tw.line("ERROR: could not load %s\n" % (e.path), red=True) + return 4 + else: + return config.hook.pytest_cmdline_main(config=config) class cmdline: # compatibility namespace main = staticmethod(main) @@ -86,8 +102,7 @@ config.addinivalue_line("markers", "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.") - while self._warnings: - warning = self._warnings.pop(0) + for warning in self._warnings: config.warn(code="I1", message=warning) @@ -496,7 +511,8 @@ continue conftestpath = parent.join("conftest.py") if conftestpath.check(file=1): - clist.append(self.importconftest(conftestpath)) + mod = self.importconftest(conftestpath) + clist.append(mod) self._path2confmods[path] = clist return clist @@ -522,7 +538,11 @@ pkgpath = conftestpath.pypkgpath() if pkgpath is None: _ensure_removed_sysmodule(conftestpath.purebasename) - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + try: + mod = conftestpath.pyimport() + except Exception: + raise ConftestImportFailure(conftestpath, sys.exc_info()) + self._conftestpath2mod[conftestpath] = mod dirpath = conftestpath.dirpath() if dirpath in self._path2confmods: for path, mods in self._path2confmods.items(): @@ -682,9 +702,19 @@ self.pluginmanager.consider_preparse(args) self.pluginmanager.consider_setuptools_entrypoints() self.pluginmanager.consider_env() - self.known_args_namespace = self._parser.parse_known_args(args) - self.hook.pytest_load_initial_conftests(early_config=self, - args=args, parser=self._parser) + self.known_args_namespace = ns = self._parser.parse_known_args(args) + try: + self.hook.pytest_load_initial_conftests(early_config=self, + args=args, parser=self._parser) + except ConftestImportFailure: + e = sys.exc_info()[1] + if ns.help or ns.version: + # we don't want to prevent --help/--version to work + # so just let is pass and print a warning at the end + self.pluginmanager._warnings.append( + "could not load initial conftests (%s)\n" % e.path) + else: + raise def _checkversion(self): import pytest diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 202e6aee58906bf7fc5313d8567bb0451497394f _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -86,17 +86,9 @@ tw.line("(shown according to specified file_or_dir or current dir " "if not specified)") for warning in config.pluginmanager._warnings: - tw.line("warning: %s" % (warning,)) + tw.line("warning: %s" % (warning,), red=True) return - tw.line("conftest.py options:") - tw.line() - conftestitems = sorted(config._parser._conftestdict.items()) - for name, help in conftest_options + conftestitems: - line = " %-15s %s" %(name, help) - tw.line(line[:tw.fullwidth]) - tw.line() - #tw.sep( "=") conftest_options = [ ('pytest_plugins', 'list of plugin names to load'), diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 202e6aee58906bf7fc5313d8567bb0451497394f testing/acceptance_test.py --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -124,6 +124,18 @@ "*ERROR: not found:*%s" %(p2.basename,) ]) + def test_issue486_better_reporting_on_conftest_load_failure(self, testdir): + testdir.makepyfile("") + testdir.makeconftest("import qwerty") + result = testdir.runpytest("--help") + result.stdout.fnmatch_lines(""" + *--version* + *warning*conftest.py* + """) + result = testdir.runpytest() + result.stderr.fnmatch_lines(""" + *ERROR*could not load*conftest.py* + """) def test_early_skip(self, testdir): https://bitbucket.org/hpk42/pytest/commits/e5b0bd42a006/ Changeset: e5b0bd42a006 User: bubenkoff Date: 2014-04-03 00:19:05 Summary: Merged in hpk42/pytest-hpk/issue486 (pull request #147) fix issue486: better reporting and handling of early conftest loading failures Affected #: 4 files diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -48,6 +48,9 @@ - fix issue493: don't run tests in doc directory with ``python setup.py test`` (use tox -e doctesting for that) +- fix issue486: better reporting and handling of early conftest loading failures + + 2.5.2 ----------------------------------- diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -7,6 +7,13 @@ from _pytest.core import PluginManager # pytest startup +# +class ConftestImportFailure(Exception): + def __init__(self, path, excinfo): + Exception.__init__(self, path, excinfo) + self.path = path + self.excinfo = excinfo + def main(args=None, plugins=None): """ return exit code, after performing an in-process test run. @@ -16,8 +23,17 @@ :arg plugins: list of plugin objects to be auto-registered during initialization. """ - config = _prepareconfig(args, plugins) - return config.hook.pytest_cmdline_main(config=config) + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure: + e = sys.exc_info()[1] + tw = py.io.TerminalWriter(sys.stderr) + for line in py.std.traceback.format_exception(*e.excinfo): + tw.line(line.rstrip(), red=True) + tw.line("ERROR: could not load %s\n" % (e.path), red=True) + return 4 + else: + return config.hook.pytest_cmdline_main(config=config) class cmdline: # compatibility namespace main = staticmethod(main) @@ -86,8 +102,7 @@ config.addinivalue_line("markers", "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.") - while self._warnings: - warning = self._warnings.pop(0) + for warning in self._warnings: config.warn(code="I1", message=warning) @@ -496,7 +511,8 @@ continue conftestpath = parent.join("conftest.py") if conftestpath.check(file=1): - clist.append(self.importconftest(conftestpath)) + mod = self.importconftest(conftestpath) + clist.append(mod) self._path2confmods[path] = clist return clist @@ -522,7 +538,11 @@ pkgpath = conftestpath.pypkgpath() if pkgpath is None: _ensure_removed_sysmodule(conftestpath.purebasename) - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + try: + mod = conftestpath.pyimport() + except Exception: + raise ConftestImportFailure(conftestpath, sys.exc_info()) + self._conftestpath2mod[conftestpath] = mod dirpath = conftestpath.dirpath() if dirpath in self._path2confmods: for path, mods in self._path2confmods.items(): @@ -682,9 +702,19 @@ self.pluginmanager.consider_preparse(args) self.pluginmanager.consider_setuptools_entrypoints() self.pluginmanager.consider_env() - self.known_args_namespace = self._parser.parse_known_args(args) - self.hook.pytest_load_initial_conftests(early_config=self, - args=args, parser=self._parser) + self.known_args_namespace = ns = self._parser.parse_known_args(args) + try: + self.hook.pytest_load_initial_conftests(early_config=self, + args=args, parser=self._parser) + except ConftestImportFailure: + e = sys.exc_info()[1] + if ns.help or ns.version: + # we don't want to prevent --help/--version to work + # so just let is pass and print a warning at the end + self.pluginmanager._warnings.append( + "could not load initial conftests (%s)\n" % e.path) + else: + raise def _checkversion(self): import pytest diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -86,17 +86,9 @@ tw.line("(shown according to specified file_or_dir or current dir " "if not specified)") for warning in config.pluginmanager._warnings: - tw.line("warning: %s" % (warning,)) + tw.line("warning: %s" % (warning,), red=True) return - tw.line("conftest.py options:") - tw.line() - conftestitems = sorted(config._parser._conftestdict.items()) - for name, help in conftest_options + conftestitems: - line = " %-15s %s" %(name, help) - tw.line(line[:tw.fullwidth]) - tw.line() - #tw.sep( "=") conftest_options = [ ('pytest_plugins', 'list of plugin names to load'), diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 testing/acceptance_test.py --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -124,6 +124,18 @@ "*ERROR: not found:*%s" %(p2.basename,) ]) + def test_issue486_better_reporting_on_conftest_load_failure(self, testdir): + testdir.makepyfile("") + testdir.makeconftest("import qwerty") + result = testdir.runpytest("--help") + result.stdout.fnmatch_lines(""" + *--version* + *warning*conftest.py* + """) + result = testdir.runpytest() + result.stderr.fnmatch_lines(""" + *ERROR*could not load*conftest.py* + """) def test_early_skip(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Thu Apr 3 09:18:23 2014 From: issues-reply at bitbucket.org (Philipp Sonntag) Date: Thu, 03 Apr 2014 07:18:23 -0000 Subject: [Pytest-commit] Issue #497: py.test fails when testing code that uses oslo.config and a config file (hpk42/pytest) Message-ID: <20140403071823.11326.97377@app08.ash-private.bitbucket.org> New issue 497: py.test fails when testing code that uses oslo.config and a config file https://bitbucket.org/hpk42/pytest/issue/497/pytest-fails-when-testing-code-that-uses Philipp Sonntag: Hello, I was refactoring a tool in a package that I'm working on, to use oslo.config for it's configuration. After that, I wanted to re-run the tests I did so far, but py.test fails with ``` #!python (...) > CONF(default_config_files=[_conffile]) /Library/Python/2.7/site-packages/oslo/config/cfg.py:1632: in __call__ > else sys.argv[1:]) /Library/Python/2.7/site-packages/oslo/config/cfg.py:2133: in _parse_cli_opts > return self._parse_config_files() /Library/Python/2.7/site-packages/oslo/config/cfg.py:2148: in _parse_config_files > return self._oparser.parse_args(self._args, namespace) /Library/Python/2.7/site-packages/oslo/config/cfg.py:1496: in parse_args > return super(_CachedArgumentParser, self).parse_args(args, namespace) /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py:1691: in parse_args > self.error(msg % ' '.join(argv)) /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py:2361: in error > self.exit(2, _('%s: error: %s\n') % (self.prog, message)) /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py:2349: in exit > _sys.exit(status) E SystemExit: 2 ------------------------------------------------------------------------------------------------- Captured stderr -------------------------------------------------------------------------------------------------- usage: py.test [-h] [--config-dir DIR] [--config-file PATH] [--version] py.test: error: unrecognized arguments: -l tests/ ============================================================================================= 1 error in 0.19 seconds ============================================================================================== ``` I cut of the first 3 lines of the traceback, cause of the package names etc, sorry about that. What happens in the package: - the module (using oslo.config) get's imported. - there's a method in it, *configure()*, that's run when imported - it set's the configuration and runs * CONF(default_config_files=[_conffile])* That seems to be where things start to fail. Without a config file being defined, the tests will run just fine. I did run that with --trace-config, the output mentions 'active plugins' and lists: *pytestconfig : <_pytest.config.Config object at 0x108a906d0>* Could a workaround be, to just 'disable' the plugin? I haven't checked for a way to do so just yet. I'm also not sure if I get 'plugin' right in this context and if this could be disabled at all. Nevertheless I'd like to keep using py.test for the tests and I'm (ok, honestly: more or less) using a TDD approach to this package. But with py.test failing . . . Sorry, if anything you need to know is missing. Just let me know and I'll update the issue. I've attached three files, a sample module, a test file for it and a sample config, that reproduce the issue. Regards, Phil BTW: I never used python unittest framework directly (through unittest.main()), but this fails for a complete different reason (import error for the package) and nose fails for the same reason as does py.test. From commits-noreply at bitbucket.org Thu Apr 3 09:38:51 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 07:38:51 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140403073851.24075.56354@app13.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/4361873fa57c/ Changeset: 4361873fa57c Branch: conftest-clean User: hpk42 Date: 2014-04-02 22:30:45 Summary: cleanup internal conftest handling and avoid the strange None entry in the conftest cache. (There is basically no reason to ask for conftestmodules without specifying a path.) Affected #: 4 files diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 4361873fa57cb7e44ee863b9b5b1570834b2cc23 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -477,7 +477,7 @@ self._try_load_conftest(current) def _try_load_conftest(self, anchor): - self._path2confmods[None] = self.getconftestmodules(anchor) + self.getconftestmodules(anchor) # let's also consider test* subdirs if anchor.check(dir=1): for x in anchor.listdir("test*"): @@ -486,10 +486,8 @@ def getconftestmodules(self, path): try: - clist = self._path2confmods[path] + return self._path2confmods[path] except KeyError: - if path is None: - raise ValueError("missing default conftest.") clist = [] for parent in path.parts(): if self._confcutdir and self._confcutdir.relto(parent): @@ -498,16 +496,11 @@ if conftestpath.check(file=1): clist.append(self.importconftest(conftestpath)) self._path2confmods[path] = clist - return clist + return clist - def rget(self, name, path=None): - mod, value = self.rget_with_confmod(name, path) - return value - - def rget_with_confmod(self, name, path=None): + def rget_with_confmod(self, name, path): modules = self.getconftestmodules(path) - modules.reverse() - for mod in modules: + for mod in reversed(modules): try: return mod, getattr(mod, name) except AttributeError: @@ -515,7 +508,6 @@ raise KeyError(name) def importconftest(self, conftestpath): - assert conftestpath.check(), conftestpath try: return self._conftestpath2mod[conftestpath] except KeyError: @@ -529,13 +521,10 @@ if path and path.relto(dirpath) or path == dirpath: assert mod not in mods mods.append(mod) - self._postimport(mod) + if self._onimport: + self._onimport(mod) return mod - def _postimport(self, mod): - if self._onimport: - self._onimport(mod) - return mod def _ensure_removed_sysmodule(modname): try: @@ -550,6 +539,7 @@ def __repr__(self): return "" %(self.__dict__,) +notset = object() FILE_OR_DIR = 'file_or_dir' class Config(object): """ access to configuration values, pluginmanager and plugin hooks. """ @@ -757,7 +747,7 @@ assert type is None return value - def _getconftest_pathlist(self, name, path=None): + def _getconftest_pathlist(self, name, path): try: mod, relroots = self._conftest.rget_with_confmod(name, path) except KeyError: @@ -771,47 +761,31 @@ l.append(relroot) return l - def _getconftest(self, name, path=None, check=False): - if check: - self._checkconftest(name) - return self._conftest.rget(name, path) - - def getoption(self, name): + def getoption(self, name, default=notset, skip=False): """ return command line option value. :arg name: name of the option. You may also specify the literal ``--OPT`` option instead of the "dest" option name. + :arg default: default value if no option of that name exists. + :arg skip: if True raise pytest.skip if not option exists. """ name = self._opt2dest.get(name, name) try: return getattr(self.option, name) except AttributeError: + if default is not notset: + return default + if skip: + py.test.skip("no %r option found" %(name,)) raise ValueError("no option named %r" % (name,)) def getvalue(self, name, path=None): - """ return command line option value. - - :arg name: name of the command line option - - (deprecated) if we can't find the option also lookup - the name in a matching conftest file. - """ - try: - return getattr(self.option, name) - except AttributeError: - return self._getconftest(name, path, check=False) + """ (deprecated, use getoption()) """ + return self.getoption(name) def getvalueorskip(self, name, path=None): - """ (deprecated) return getvalue(name) or call - pytest.skip if no value exists. """ - __tracebackhide__ = True - try: - val = self.getvalue(name, path) - if val is None: - raise KeyError(name) - return val - except KeyError: - py.test.skip("no %r value found" %(name,)) + """ (deprecated, use getoption(skip=True)) """ + return self.getoption(name, skip=True) def exists(path, ignore=EnvironmentError): try: diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 4361873fa57cb7e44ee863b9b5b1570834b2cc23 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -144,7 +144,7 @@ p = path.dirpath() ignore_paths = config._getconftest_pathlist("collect_ignore", path=p) ignore_paths = ignore_paths or [] - excludeopt = config.getvalue("ignore") + excludeopt = config.getoption("ignore") if excludeopt: ignore_paths.extend([py.path.local(x) for x in excludeopt]) return path in ignore_paths diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 4361873fa57cb7e44ee863b9b5b1570834b2cc23 testing/test_config.py --- a/testing/test_config.py +++ b/testing/test_config.py @@ -89,20 +89,6 @@ assert len(l) == 1 assert l[0] == "hello [config]\n" - def test_config_getvalue_honours_conftest(self, testdir): - testdir.makepyfile(conftest="x=1") - testdir.mkdir("sub").join("conftest.py").write("x=2 ; y = 3") - config = testdir.parseconfig() - o = testdir.tmpdir - assert config.getvalue("x") == 1 - assert config.getvalue("x", o.join('sub')) == 2 - pytest.raises(KeyError, "config.getvalue('y')") - config = testdir.parseconfigure(str(o.join('sub'))) - assert config.getvalue("x") == 2 - assert config.getvalue("y") == 3 - assert config.getvalue("x", o) == 1 - pytest.raises(KeyError, 'config.getvalue("y", o)') - def test_config_getoption(self, testdir): testdir.makeconftest(""" def pytest_addoption(parser): @@ -130,34 +116,20 @@ "config.getvalueorskip('hello')") verbose = config.getvalueorskip("verbose") assert verbose == config.option.verbose - config.option.hello = None - try: - config.getvalueorskip('hello') - except KeyboardInterrupt: - raise - except: - excinfo = py.code.ExceptionInfo() - frame = excinfo.traceback[-2].frame - assert frame.code.name == "getvalueorskip" - assert frame.eval("__tracebackhide__") - def test_config_overwrite(self, testdir): - o = testdir.tmpdir - o.ensure("conftest.py").write("x=1") - config = testdir.parseconfig(str(o)) - assert config.getvalue('x') == 1 - config.option.x = 2 - assert config.getvalue('x') == 2 - config = testdir.parseconfig(str(o)) - assert config.getvalue('x') == 1 + def test_getoption(self, testdir): + config = testdir.parseconfig() + with pytest.raises(ValueError): + config.getvalue('x') + assert config.getoption("x", 1) == 1 def test_getconftest_pathlist(self, testdir, tmpdir): somepath = tmpdir.join("x", "y", "z") p = tmpdir.join("conftest.py") p.write("pathlist = ['.', %r]" % str(somepath)) config = testdir.parseconfigure(p) - assert config._getconftest_pathlist('notexist') is None - pl = config._getconftest_pathlist('pathlist') + assert config._getconftest_pathlist('notexist', path=tmpdir) is None + pl = config._getconftest_pathlist('pathlist', path=tmpdir) print(pl) assert len(pl) == 2 assert pl[0] == tmpdir diff -r ceafafc62657ed15b0486e8838f20b06abc0880d -r 4361873fa57cb7e44ee863b9b5b1570834b2cc23 testing/test_conftest.py --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,22 +1,17 @@ import py, pytest from _pytest.config import Conftest -def pytest_generate_tests(metafunc): - if "basedir" in metafunc.fixturenames: - metafunc.addcall(param="global") - metafunc.addcall(param="inpackage") -def pytest_funcarg__basedir(request): - def basedirmaker(request): - d = request.getfuncargvalue("tmpdir") - d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") - d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") - if request.param == "inpackage": - d.ensure("adir/__init__.py") - d.ensure("adir/b/__init__.py") - return d - return request.cached_setup( - lambda: basedirmaker(request), extrakey=request.param) + at pytest.fixture(scope="module", params=["global", "inpackage"]) +def basedir(request): + from _pytest.tmpdir import tmpdir + tmpdir = tmpdir(request) + tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3") + tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") + if request.param == "inpackage": + tmpdir.ensure("adir/__init__.py") + tmpdir.ensure("adir/b/__init__.py") + return tmpdir def ConftestWithSetinitial(path): conftest = Conftest() @@ -33,17 +28,17 @@ class TestConftestValueAccessGlobal: def test_basic_init(self, basedir): conftest = Conftest() - conftest_setinitial(conftest, [basedir.join("adir")]) - assert conftest.rget("a") == 1 + p = basedir.join("adir") + assert conftest.rget_with_confmod("a", p)[1] == 1 def test_onimport(self, basedir): l = [] conftest = Conftest(onimport=l.append) - conftest_setinitial(conftest, [basedir.join("adir"), - '--confcutdir=%s' % basedir]) + adir = basedir.join("adir") + conftest_setinitial(conftest, [adir], confcutdir=basedir) assert len(l) == 1 - assert conftest.rget("a") == 1 - assert conftest.rget("b", basedir.join("adir", "b")) == 2 + assert conftest.rget_with_confmod("a", adir)[1] == 1 + assert conftest.rget_with_confmod("b", adir.join("b"))[1] == 2 assert len(l) == 2 def test_immediate_initialiation_and_incremental_are_the_same(self, basedir): @@ -57,37 +52,16 @@ conftest.getconftestmodules(basedir.join('b')) assert len(conftest._path2confmods) == snap1 + 2 - def test_default_has_lower_prio(self, basedir): - conftest = ConftestWithSetinitial(basedir.join("adir")) - assert conftest.rget('Directory') == 3 - #assert conftest.lget('Directory') == pytest.Directory - def test_value_access_not_existing(self, basedir): conftest = ConftestWithSetinitial(basedir) - pytest.raises(KeyError, lambda: conftest.rget('a')) - #pytest.raises(KeyError, "conftest.lget('a')") + with pytest.raises(KeyError): + conftest.rget_with_confmod('a', basedir) def test_value_access_by_path(self, basedir): conftest = ConftestWithSetinitial(basedir) - assert conftest.rget("a", basedir.join('adir')) == 1 - #assert conftest.lget("a", basedir.join('adir')) == 1 - assert conftest.rget("a", basedir.join('adir', 'b')) == 1.5 - #assert conftest.lget("a", basedir.join('adir', 'b')) == 1 - #assert conftest.lget("b", basedir.join('adir', 'b')) == 2 - #assert pytest.raises(KeyError, - # 'conftest.lget("b", basedir.join("a"))' - #) - - def test_value_access_with_init_one_conftest(self, basedir): - conftest = ConftestWithSetinitial(basedir.join('adir')) - assert conftest.rget("a") == 1 - #assert conftest.lget("a") == 1 - - def test_value_access_with_init_two_conftests(self, basedir): - conftest = ConftestWithSetinitial(basedir.join("adir", "b")) - conftest.rget("a") == 1.5 - #conftest.lget("a") == 1 - #conftest.lget("b") == 1 + adir = basedir.join("adir") + assert conftest.rget_with_confmod("a", adir)[1] == 1 + assert conftest.rget_with_confmod("a", adir.join("b"))[1] == 1.5 def test_value_access_with_confmod(self, basedir): startdir = basedir.join("adir", "b") @@ -111,7 +85,7 @@ conf.join("conftest.py").ensure() conftest = Conftest() conftest_setinitial(conftest, [conf.basename, conf.basename]) - l = conftest.getconftestmodules(None) + l = conftest.getconftestmodules(conf) assert len(l) == 1 def test_issue151_load_all_conftests(testdir): https://bitbucket.org/hpk42/pytest/commits/dd7b468026d1/ Changeset: dd7b468026d1 User: bubenkoff Date: 2014-04-03 09:38:47 Summary: Merged in hpk42/pytest-hpk/conftest-clean (pull request #148) cleanup internal conftest handling and avoid the strange None entry in the conftest cache. Affected #: 4 files diff -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 -r dd7b468026d15a577d2d1f272257e86b2855eb83 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -492,7 +492,7 @@ self._try_load_conftest(current) def _try_load_conftest(self, anchor): - self._path2confmods[None] = self.getconftestmodules(anchor) + self.getconftestmodules(anchor) # let's also consider test* subdirs if anchor.check(dir=1): for x in anchor.listdir("test*"): @@ -501,10 +501,8 @@ def getconftestmodules(self, path): try: - clist = self._path2confmods[path] + return self._path2confmods[path] except KeyError: - if path is None: - raise ValueError("missing default conftest.") clist = [] for parent in path.parts(): if self._confcutdir and self._confcutdir.relto(parent): @@ -514,16 +512,11 @@ mod = self.importconftest(conftestpath) clist.append(mod) self._path2confmods[path] = clist - return clist + return clist - def rget(self, name, path=None): - mod, value = self.rget_with_confmod(name, path) - return value - - def rget_with_confmod(self, name, path=None): + def rget_with_confmod(self, name, path): modules = self.getconftestmodules(path) - modules.reverse() - for mod in modules: + for mod in reversed(modules): try: return mod, getattr(mod, name) except AttributeError: @@ -531,7 +524,6 @@ raise KeyError(name) def importconftest(self, conftestpath): - assert conftestpath.check(), conftestpath try: return self._conftestpath2mod[conftestpath] except KeyError: @@ -549,13 +541,10 @@ if path and path.relto(dirpath) or path == dirpath: assert mod not in mods mods.append(mod) - self._postimport(mod) + if self._onimport: + self._onimport(mod) return mod - def _postimport(self, mod): - if self._onimport: - self._onimport(mod) - return mod def _ensure_removed_sysmodule(modname): try: @@ -570,6 +559,7 @@ def __repr__(self): return "" %(self.__dict__,) +notset = object() FILE_OR_DIR = 'file_or_dir' class Config(object): """ access to configuration values, pluginmanager and plugin hooks. """ @@ -787,7 +777,7 @@ assert type is None return value - def _getconftest_pathlist(self, name, path=None): + def _getconftest_pathlist(self, name, path): try: mod, relroots = self._conftest.rget_with_confmod(name, path) except KeyError: @@ -801,47 +791,31 @@ l.append(relroot) return l - def _getconftest(self, name, path=None, check=False): - if check: - self._checkconftest(name) - return self._conftest.rget(name, path) - - def getoption(self, name): + def getoption(self, name, default=notset, skip=False): """ return command line option value. :arg name: name of the option. You may also specify the literal ``--OPT`` option instead of the "dest" option name. + :arg default: default value if no option of that name exists. + :arg skip: if True raise pytest.skip if not option exists. """ name = self._opt2dest.get(name, name) try: return getattr(self.option, name) except AttributeError: + if default is not notset: + return default + if skip: + py.test.skip("no %r option found" %(name,)) raise ValueError("no option named %r" % (name,)) def getvalue(self, name, path=None): - """ return command line option value. - - :arg name: name of the command line option - - (deprecated) if we can't find the option also lookup - the name in a matching conftest file. - """ - try: - return getattr(self.option, name) - except AttributeError: - return self._getconftest(name, path, check=False) + """ (deprecated, use getoption()) """ + return self.getoption(name) def getvalueorskip(self, name, path=None): - """ (deprecated) return getvalue(name) or call - pytest.skip if no value exists. """ - __tracebackhide__ = True - try: - val = self.getvalue(name, path) - if val is None: - raise KeyError(name) - return val - except KeyError: - py.test.skip("no %r value found" %(name,)) + """ (deprecated, use getoption(skip=True)) """ + return self.getoption(name, skip=True) def exists(path, ignore=EnvironmentError): try: diff -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 -r dd7b468026d15a577d2d1f272257e86b2855eb83 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -144,7 +144,7 @@ p = path.dirpath() ignore_paths = config._getconftest_pathlist("collect_ignore", path=p) ignore_paths = ignore_paths or [] - excludeopt = config.getvalue("ignore") + excludeopt = config.getoption("ignore") if excludeopt: ignore_paths.extend([py.path.local(x) for x in excludeopt]) return path in ignore_paths diff -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 -r dd7b468026d15a577d2d1f272257e86b2855eb83 testing/test_config.py --- a/testing/test_config.py +++ b/testing/test_config.py @@ -89,20 +89,6 @@ assert len(l) == 1 assert l[0] == "hello [config]\n" - def test_config_getvalue_honours_conftest(self, testdir): - testdir.makepyfile(conftest="x=1") - testdir.mkdir("sub").join("conftest.py").write("x=2 ; y = 3") - config = testdir.parseconfig() - o = testdir.tmpdir - assert config.getvalue("x") == 1 - assert config.getvalue("x", o.join('sub')) == 2 - pytest.raises(KeyError, "config.getvalue('y')") - config = testdir.parseconfigure(str(o.join('sub'))) - assert config.getvalue("x") == 2 - assert config.getvalue("y") == 3 - assert config.getvalue("x", o) == 1 - pytest.raises(KeyError, 'config.getvalue("y", o)') - def test_config_getoption(self, testdir): testdir.makeconftest(""" def pytest_addoption(parser): @@ -130,34 +116,20 @@ "config.getvalueorskip('hello')") verbose = config.getvalueorskip("verbose") assert verbose == config.option.verbose - config.option.hello = None - try: - config.getvalueorskip('hello') - except KeyboardInterrupt: - raise - except: - excinfo = py.code.ExceptionInfo() - frame = excinfo.traceback[-2].frame - assert frame.code.name == "getvalueorskip" - assert frame.eval("__tracebackhide__") - def test_config_overwrite(self, testdir): - o = testdir.tmpdir - o.ensure("conftest.py").write("x=1") - config = testdir.parseconfig(str(o)) - assert config.getvalue('x') == 1 - config.option.x = 2 - assert config.getvalue('x') == 2 - config = testdir.parseconfig(str(o)) - assert config.getvalue('x') == 1 + def test_getoption(self, testdir): + config = testdir.parseconfig() + with pytest.raises(ValueError): + config.getvalue('x') + assert config.getoption("x", 1) == 1 def test_getconftest_pathlist(self, testdir, tmpdir): somepath = tmpdir.join("x", "y", "z") p = tmpdir.join("conftest.py") p.write("pathlist = ['.', %r]" % str(somepath)) config = testdir.parseconfigure(p) - assert config._getconftest_pathlist('notexist') is None - pl = config._getconftest_pathlist('pathlist') + assert config._getconftest_pathlist('notexist', path=tmpdir) is None + pl = config._getconftest_pathlist('pathlist', path=tmpdir) print(pl) assert len(pl) == 2 assert pl[0] == tmpdir diff -r e5b0bd42a00632fec91a70fb8c61edcd7d38e898 -r dd7b468026d15a577d2d1f272257e86b2855eb83 testing/test_conftest.py --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,22 +1,17 @@ import py, pytest from _pytest.config import Conftest -def pytest_generate_tests(metafunc): - if "basedir" in metafunc.fixturenames: - metafunc.addcall(param="global") - metafunc.addcall(param="inpackage") -def pytest_funcarg__basedir(request): - def basedirmaker(request): - d = request.getfuncargvalue("tmpdir") - d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") - d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") - if request.param == "inpackage": - d.ensure("adir/__init__.py") - d.ensure("adir/b/__init__.py") - return d - return request.cached_setup( - lambda: basedirmaker(request), extrakey=request.param) + at pytest.fixture(scope="module", params=["global", "inpackage"]) +def basedir(request): + from _pytest.tmpdir import tmpdir + tmpdir = tmpdir(request) + tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3") + tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") + if request.param == "inpackage": + tmpdir.ensure("adir/__init__.py") + tmpdir.ensure("adir/b/__init__.py") + return tmpdir def ConftestWithSetinitial(path): conftest = Conftest() @@ -33,17 +28,17 @@ class TestConftestValueAccessGlobal: def test_basic_init(self, basedir): conftest = Conftest() - conftest_setinitial(conftest, [basedir.join("adir")]) - assert conftest.rget("a") == 1 + p = basedir.join("adir") + assert conftest.rget_with_confmod("a", p)[1] == 1 def test_onimport(self, basedir): l = [] conftest = Conftest(onimport=l.append) - conftest_setinitial(conftest, [basedir.join("adir"), - '--confcutdir=%s' % basedir]) + adir = basedir.join("adir") + conftest_setinitial(conftest, [adir], confcutdir=basedir) assert len(l) == 1 - assert conftest.rget("a") == 1 - assert conftest.rget("b", basedir.join("adir", "b")) == 2 + assert conftest.rget_with_confmod("a", adir)[1] == 1 + assert conftest.rget_with_confmod("b", adir.join("b"))[1] == 2 assert len(l) == 2 def test_immediate_initialiation_and_incremental_are_the_same(self, basedir): @@ -57,37 +52,16 @@ conftest.getconftestmodules(basedir.join('b')) assert len(conftest._path2confmods) == snap1 + 2 - def test_default_has_lower_prio(self, basedir): - conftest = ConftestWithSetinitial(basedir.join("adir")) - assert conftest.rget('Directory') == 3 - #assert conftest.lget('Directory') == pytest.Directory - def test_value_access_not_existing(self, basedir): conftest = ConftestWithSetinitial(basedir) - pytest.raises(KeyError, lambda: conftest.rget('a')) - #pytest.raises(KeyError, "conftest.lget('a')") + with pytest.raises(KeyError): + conftest.rget_with_confmod('a', basedir) def test_value_access_by_path(self, basedir): conftest = ConftestWithSetinitial(basedir) - assert conftest.rget("a", basedir.join('adir')) == 1 - #assert conftest.lget("a", basedir.join('adir')) == 1 - assert conftest.rget("a", basedir.join('adir', 'b')) == 1.5 - #assert conftest.lget("a", basedir.join('adir', 'b')) == 1 - #assert conftest.lget("b", basedir.join('adir', 'b')) == 2 - #assert pytest.raises(KeyError, - # 'conftest.lget("b", basedir.join("a"))' - #) - - def test_value_access_with_init_one_conftest(self, basedir): - conftest = ConftestWithSetinitial(basedir.join('adir')) - assert conftest.rget("a") == 1 - #assert conftest.lget("a") == 1 - - def test_value_access_with_init_two_conftests(self, basedir): - conftest = ConftestWithSetinitial(basedir.join("adir", "b")) - conftest.rget("a") == 1.5 - #conftest.lget("a") == 1 - #conftest.lget("b") == 1 + adir = basedir.join("adir") + assert conftest.rget_with_confmod("a", adir)[1] == 1 + assert conftest.rget_with_confmod("a", adir.join("b"))[1] == 1.5 def test_value_access_with_confmod(self, basedir): startdir = basedir.join("adir", "b") @@ -111,7 +85,7 @@ conf.join("conftest.py").ensure() conftest = Conftest() conftest_setinitial(conftest, [conf.basename, conf.basename]) - l = conftest.getconftestmodules(None) + l = conftest.getconftestmodules(conf) assert len(l) == 1 def test_issue151_load_all_conftests(testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 09:47:45 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 07:47:45 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140403074745.18504.57677@app02.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/c46eb47ecff4/ Changeset: c46eb47ecff4 Branch: break_ExceptionInfo_reference_cycles User: jurko Date: 2014-04-02 15:34:36 Summary: break reference cycles caused by storing local reference to exception info Such reference cycles unnecessarily cause Python interpreter not to garbage collect the objects referenced in those cycles as soon they could be collected, and in turn cause the tests to use more memory than is strictly necessary. Affected #: 3 files diff -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 -r c46eb47ecff4289ac318426f871a20f1aa7d2467 _pytest/assertion/util.py --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -149,11 +149,10 @@ if istext(left) and istext(right): explanation = _notin_text(left, right, verbose) except Exception: - excinfo = py.code.ExceptionInfo() explanation = [ u('(pytest_assertion plugin: representation of details failed. ' 'Probably an object has a faulty __repr__.)'), - u(excinfo)] + u(py.code.ExceptionInfo())] if not explanation: return None diff -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 -r c46eb47ecff4289ac318426f871a20f1aa7d2467 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -98,6 +98,7 @@ if session._testsfailed: session.exitstatus = EXIT_TESTSFAILED finally: + excinfo = None # Explicitly break reference cycle. session.startdir.chdir() if initstate >= 2: config.hook.pytest_sessionfinish( diff -r e3309a39fb919391d0e9d9cb0e7fc7e75b7162e7 -r c46eb47ecff4289ac318426f871a20f1aa7d2467 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -456,8 +456,8 @@ try: mod = self.fspath.pyimport(ensuresyspath=True) except SyntaxError: - excinfo = py.code.ExceptionInfo() - raise self.CollectError(excinfo.getrepr(style="short")) + raise self.CollectError( + py.code.ExceptionInfo().getrepr(style="short")) except self.fspath.ImportMismatchError: e = sys.exc_info()[1] raise self.CollectError( https://bitbucket.org/hpk42/pytest/commits/e3398dc8d7cc/ Changeset: e3398dc8d7cc User: hpk42 Date: 2014-04-03 09:47:41 Summary: Merged in jurko/pytest/break_ExceptionInfo_reference_cycles (pull request #144) break reference cycles caused by storing local reference to exception info Affected #: 3 files diff -r dd7b468026d15a577d2d1f272257e86b2855eb83 -r e3398dc8d7cc36221657d3d4b417231f60a019a6 _pytest/assertion/util.py --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -149,11 +149,10 @@ if istext(left) and istext(right): explanation = _notin_text(left, right, verbose) except Exception: - excinfo = py.code.ExceptionInfo() explanation = [ u('(pytest_assertion plugin: representation of details failed. ' 'Probably an object has a faulty __repr__.)'), - u(excinfo)] + u(py.code.ExceptionInfo())] if not explanation: return None diff -r dd7b468026d15a577d2d1f272257e86b2855eb83 -r e3398dc8d7cc36221657d3d4b417231f60a019a6 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -98,6 +98,7 @@ if session._testsfailed: session.exitstatus = EXIT_TESTSFAILED finally: + excinfo = None # Explicitly break reference cycle. session.startdir.chdir() if initstate >= 2: config.hook.pytest_sessionfinish( diff -r dd7b468026d15a577d2d1f272257e86b2855eb83 -r e3398dc8d7cc36221657d3d4b417231f60a019a6 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -456,8 +456,8 @@ try: mod = self.fspath.pyimport(ensuresyspath=True) except SyntaxError: - excinfo = py.code.ExceptionInfo() - raise self.CollectError(excinfo.getrepr(style="short")) + raise self.CollectError( + py.code.ExceptionInfo().getrepr(style="short")) except self.fspath.ImportMismatchError: e = sys.exc_info()[1] raise self.CollectError( Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 10:00:27 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 08:00:27 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140403080027.11336.67033@app09.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/92078d772e04/ Changeset: 92078d772e04 Branch: makepyfile_utf8 User: Marc Abramowitz Date: 2014-03-28 17:44:18 Summary: Make makepyfile accept UTF-8 so a few cookie tests in test_assertrewrite.py don't need to be dedented. Affected #: 2 files diff -r d4b093bc36df977c017b43286953286542c788cf -r 92078d772e043323ff09b9b5b25c3baf0745a820 _pytest/pytester.py --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -246,8 +246,14 @@ ret = None for name, value in items: p = self.tmpdir.join(name).new(ext=ext) - source = py.builtin._totext(py.code.Source(value)).strip() - content = source.encode("utf-8") # + "\n" + source = py.code.Source(value) + def my_totext(s, encoding="utf-8"): + if py.builtin._isbytes(s): + s = py.builtin._totext(s, encoding=encoding) + return s + source_unicode = "\n".join([my_totext(line) for line in source.lines]) + source = py.builtin._totext(source_unicode) + content = source.strip().encode("utf-8") # + "\n" #content = content.rstrip() + "\n" p.write(content, "wb") if ret is None: diff -r d4b093bc36df977c017b43286953286542c788cf -r 92078d772e043323ff09b9b5b25c3baf0745a820 testing/test_assertrewrite.py --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -458,28 +458,29 @@ @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""", "wb") + testdir.makepyfile(test_cookie=u""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""".encode('utf-8')) assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_second_line(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""#!/usr/bin/env python -# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""", "wb") + testdir.makepyfile(test_cookie=u""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""".encode('utf-8')) assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_crlf(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""#!/usr/bin/env python -# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""".replace("\n", "\r\n"), "wb") + testdir.makepyfile(test_cookie=u""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""".encode('utf-8')) assert testdir.runpytest().ret == 0 def test_sys_meta_path_munged(self, testdir): https://bitbucket.org/hpk42/pytest/commits/a2e75db956eb/ Changeset: a2e75db956eb Branch: makepyfile_utf8 User: Marc Abramowitz Date: 2014-03-28 20:11:33 Summary: Simplify tests; don't use u literal not supported in py32 Affected #: 1 file diff -r 92078d772e043323ff09b9b5b25c3baf0745a820 -r a2e75db956eb29bdf66ff001c2d14d09555a8a86 testing/test_assertrewrite.py --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -458,29 +458,29 @@ @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie(self, testdir): - testdir.makepyfile(test_cookie=u""" + testdir.makepyfile(test_cookie=""" # -*- coding: utf-8 -*- u"St\xc3\xa4d" def test_rewritten(): - assert "@py_builtins" in globals()""".encode('utf-8')) + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_second_line(self, testdir): - testdir.makepyfile(test_cookie=u""" + testdir.makepyfile(test_cookie=""" # -*- coding: utf-8 -*- u"St\xc3\xa4d" def test_rewritten(): - assert "@py_builtins" in globals()""".encode('utf-8')) + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_crlf(self, testdir): - testdir.makepyfile(test_cookie=u""" + testdir.makepyfile(test_cookie=""" # -*- coding: utf-8 -*- u"St\xc3\xa4d" def test_rewritten(): - assert "@py_builtins" in globals()""".encode('utf-8')) + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 def test_sys_meta_path_munged(self, testdir): https://bitbucket.org/hpk42/pytest/commits/f2bec97a11fa/ Changeset: f2bec97a11fa User: hpk42 Date: 2014-04-03 10:00:24 Summary: Merged in msabramo/pytest/makepyfile_utf8 (pull request #134) Make makepyfile accept UTF-8 so a few cookie tests in test_assertrewrite.py Affected #: 2 files diff -r e3398dc8d7cc36221657d3d4b417231f60a019a6 -r f2bec97a11fa7499aaed3588daf6fea24fccb999 _pytest/pytester.py --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -246,8 +246,14 @@ ret = None for name, value in items: p = self.tmpdir.join(name).new(ext=ext) - source = py.builtin._totext(py.code.Source(value)).strip() - content = source.encode("utf-8") # + "\n" + source = py.code.Source(value) + def my_totext(s, encoding="utf-8"): + if py.builtin._isbytes(s): + s = py.builtin._totext(s, encoding=encoding) + return s + source_unicode = "\n".join([my_totext(line) for line in source.lines]) + source = py.builtin._totext(source_unicode) + content = source.strip().encode("utf-8") # + "\n" #content = content.rstrip() + "\n" p.write(content, "wb") if ret is None: diff -r e3398dc8d7cc36221657d3d4b417231f60a019a6 -r f2bec97a11fa7499aaed3588daf6fea24fccb999 testing/test_assertrewrite.py --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -458,28 +458,29 @@ @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""", "wb") + testdir.makepyfile(test_cookie=""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_second_line(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""#!/usr/bin/env python -# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""", "wb") + testdir.makepyfile(test_cookie=""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 @pytest.mark.skipif("sys.version_info[0] >= 3") def test_detect_coding_cookie_crlf(self, testdir): - testdir.tmpdir.join("test_cookie.py").write("""#!/usr/bin/env python -# -*- coding: utf-8 -*- -u"St\xc3\xa4d" -def test_rewritten(): - assert "@py_builtins" in globals()""".replace("\n", "\r\n"), "wb") + testdir.makepyfile(test_cookie=""" + # -*- coding: utf-8 -*- + u"St\xc3\xa4d" + def test_rewritten(): + assert "@py_builtins" in globals()""") assert testdir.runpytest().ret == 0 def test_sys_meta_path_munged(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 10:02:04 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 08:02:04 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140403080204.12500.92249@app10.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/25bc2d6682eb/ Changeset: 25bc2d6682eb Branch: fix_capfd_fixture_docstring User: jurko Date: 2014-04-03 09:59:04 Summary: correct a capfd fixture docstring typo Affected #: 1 file diff -r e3398dc8d7cc36221657d3d4b417231f60a019a6 -r 25bc2d6682eb782873a39cc05ee91ef2b367da09 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -172,7 +172,7 @@ @pytest.fixture def capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes - captured output available via ``capsys.readouterr()`` method calls + captured output available via ``capfd.readouterr()`` method calls which return a ``(out, err)`` tuple. """ if "capsys" in request._funcargs: https://bitbucket.org/hpk42/pytest/commits/994826a51966/ Changeset: 994826a51966 User: hpk42 Date: 2014-04-03 10:02:00 Summary: Merged in jurko/pytest/fix_capfd_fixture_docstring (pull request #149) correct a capfd fixture docstring typo Affected #: 1 file diff -r f2bec97a11fa7499aaed3588daf6fea24fccb999 -r 994826a51966f862fa16b9cee6f1aa4a95f1f9e9 _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -172,7 +172,7 @@ @pytest.fixture def capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes - captured output available via ``capsys.readouterr()`` method calls + captured output available via ``capfd.readouterr()`` method calls which return a ``(out, err)`` tuple. """ if "capsys" in request._funcargs: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 10:04:17 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 08:04:17 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: add some changelog entries Message-ID: <20140403080417.7918.20734@app15.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/b5ef679ae095/ Changeset: b5ef679ae095 User: hpk42 Date: 2014-04-03 10:04:09 Summary: add some changelog entries Affected #: 1 file diff -r 994826a51966f862fa16b9cee6f1aa4a95f1f9e9 -r b5ef679ae0950d3baed67c96cd3cd13589e2e98b CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,11 @@ - fix issue486: better reporting and handling of early conftest loading failures +- some cleanup and simplification of internal conftest handling. + +- work a bit harder to break reference cycles when catching exceptions. + Thanks Jurko Gospodnetic. + 2.5.2 ----------------------------------- Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Thu Apr 3 14:56:37 2014 From: issues-reply at bitbucket.org (Alexander Steinert) Date: Thu, 03 Apr 2014 12:56:37 -0000 Subject: [Pytest-commit] Issue #498: Function scope fixture with failing finalizer called only once (hpk42/pytest) Message-ID: <20140403125637.28688.24418@app05.ash-private.bitbucket.org> New issue 498: Function scope fixture with failing finalizer called only once https://bitbucket.org/hpk42/pytest/issue/498/function-scope-fixture-with-failing Alexander Steinert: ``` python import pytest class Helper: pass @pytest.fixture(scope="function") def failing_fixture(request): helper = Helper() def fin(): raise RuntimeError("On purpose!") request.addfinalizer(fin) return helper def test_0(failing_fixture): print("test_0", failing_fixture) def test_1(failing_fixture): # In pytest 2.3.5 and 2.5.2, this test gets the same helper instance like # test_0 and the fixture's finalizer is not called. print("test_1", failing_fixture) assert 0 ``` ``` ============================= test session starts ============================== platform linux2 -- Python 3.2.3 -- py-1.4.20 -- pytest-2.5.2 collected 2 items test_pytest.py .EF ==================================== ERRORS ==================================== _________________________ ERROR at teardown of test_0 __________________________ def fin(): > raise RuntimeError("On purpose!") E RuntimeError: On purpose! test_pytest.py:12: RuntimeError ------------------------------- Captured stdout -------------------------------- test_0 =================================== FAILURES =================================== ____________________________________ test_1 ____________________________________ failing_fixture = def test_1(failing_fixture): # In pytest 2.3.5 and 2.5.2, this test gets the same helper instance like # test_0 and the fixture's finalizer is not called. print("test_1", failing_fixture) > assert 0 E assert 0 test_pytest.py:25: AssertionError ------------------------------- Captured stdout -------------------------------- test_1 ================= 1 failed, 1 passed, 1 error in 0.03 seconds ================== ``` From commits-noreply at bitbucket.org Thu Apr 3 22:26:18 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 20:26:18 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue443: fix skip examples to use proper comparison. Thanks Alex Message-ID: <20140403202618.30416.36850@app09.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/2c5cab474bb7/ Changeset: 2c5cab474bb7 User: hpk42 Date: 2014-04-03 22:26:10 Summary: fix issue443: fix skip examples to use proper comparison. Thanks Alex Groenholm. Affected #: 2 files diff -r b5ef679ae0950d3baed67c96cd3cd13589e2e98b -r 2c5cab474bb76867fb6ac0f32b4b9276aa5696c7 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -55,6 +55,9 @@ - work a bit harder to break reference cycles when catching exceptions. Thanks Jurko Gospodnetic. +- fix issue443: fix skip examples to use proper comparison. Thanks Alex + Groenholm. + 2.5.2 ----------------------------------- diff -r b5ef679ae0950d3baed67c96cd3cd13589e2e98b -r 2c5cab474bb76867fb6ac0f32b4b9276aa5696c7 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -35,7 +35,7 @@ when run on a Python3.3 interpreter:: import sys - @pytest.mark.skipif(sys.version_info >= (3,3), + @pytest.mark.skipif(sys.version_info < (3,3), reason="requires python3.3") def test_function(): ... @@ -51,7 +51,7 @@ # content of test_mymodule.py import mymodule - minversion = pytest.mark.skipif(mymodule.__versioninfo__ >= (1,1), + minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1), reason="at least mymodule-1.1 required") @minversion def test_function(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 3 22:27:11 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 03 Apr 2014 20:27:11 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: bump version to 2.6.0.dev1 because i think we are going for a 2.6.0 release next Message-ID: <20140403202711.8460.63799@app03.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/8b3cd3ae724f/ Changeset: 8b3cd3ae724f User: hpk42 Date: 2014-04-03 22:27:04 Summary: bump version to 2.6.0.dev1 because i think we are going for a 2.6.0 release next and not just a 2.5 maintenance one. Affected #: 2 files diff -r 2c5cab474bb76867fb6ac0f32b4b9276aa5696c7 -r 8b3cd3ae724fbaa527cba25af3243f6f872cb250 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.5.3.dev1' +__version__ = '2.6.0.dev1' diff -r 2c5cab474bb76867fb6ac0f32b4b9276aa5696c7 -r 8b3cd3ae724fbaa527cba25af3243f6f872cb250 setup.py --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ name='pytest', description='pytest: simple powerful testing with Python', long_description = long_description, - version='2.5.3.dev1', + version='2.6.0.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Thu Apr 3 23:15:01 2014 From: issues-reply at bitbucket.org (Marc Abramowitz) Date: Thu, 03 Apr 2014 21:15:01 -0000 Subject: [Pytest-commit] Issue #164: If install_command raises an OSError, get a traceback that doesn't tell you what failed (hpk42/tox) Message-ID: <20140403211501.17498.8747@app15.ash-private.bitbucket.org> New issue 164: If install_command raises an OSError, get a traceback that doesn't tell you what failed https://bitbucket.org/hpk42/tox/issue/164/if-install_command-raises-an-oserror-get-a Marc Abramowitz: ``` $ cat install_command_error.ini [testenv] install_command = ./LICENSE {packages} deps = pytest commands = py.test ``` ``` $ tox -c install_command_error.ini GLOB sdist-make: /Users/marca/dev/hg-repos/tox/setup.py python create: /Users/marca/dev/hg-repos/tox/.tox/python python installdeps: pytest Traceback (most recent call last): File "/Users/marca/python/virtualenvs/tox/bin/tox", line 9, in load_entry_point('tox==1.7.1', 'console_scripts', 'tox')() File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 26, in main retcode = Session(config).runcommand() File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 303, in runcommand return self.subcommand_test() File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 440, in subcommand_test if self.setupenv(venv): File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 360, in setupenv status = venv.update(action=action) File "/Users/marca/dev/hg-repos/tox/tox/_venv.py", line 142, in update self.install_deps(action) File "/Users/marca/dev/hg-repos/tox/tox/_venv.py", line 255, in install_deps self._install(deps, action=action) File "/Users/marca/dev/hg-repos/tox/tox/_venv.py", line 318, in _install action=action, extraenv=extraenv) File "/Users/marca/dev/hg-repos/tox/tox/_venv.py", line 285, in run_install_command extraenv=env, action=action) File "/Users/marca/dev/hg-repos/tox/tox/_venv.py", line 367, in _pcall return action.popen(args, cwd=cwd, env=env, redirect=redirect) File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 97, in popen stdout=f, stderr=STDOUT) File "/Users/marca/dev/hg-repos/tox/tox/_cmdline.py", line 155, in _popen stdout=stdout, stderr=stderr, env=env) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 709, in __init__ errread, errwrite) File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1326, in _execute_child raise child_exception OSError: [Errno 13] Permission denied ``` Note that the traceback doesn't tell you what the problem is -- that the `install_command` ("`./LICENSE`") was not executable. From issues-reply at bitbucket.org Fri Apr 4 07:52:41 2014 From: issues-reply at bitbucket.org (=?utf-8?q?Jurko_Gospodneti=C4=87?=) Date: Fri, 04 Apr 2014 05:52:41 -0000 Subject: [Pytest-commit] Issue #499: Selecting a test by its node id not documented (hpk42/pytest) Message-ID: <20140404055241.30132.1480@app13.ash-private.bitbucket.org> New issue 499: Selecting a test by its node id not documented https://bitbucket.org/hpk42/pytest/issue/499/selecting-a-test-by-its-node-id-not Jurko Gospodneti?: Selecting a test by its ``node id`` is not documented anywhere. This issue has been extracted out of issue #387 because the original issue got marked as resolved. The following is a copy/paste of a [related message in the original issue](../issue/387#comment-9444052): ``` In this specific case one can already select just a single test by using the node ID. Assuming these live in the test_foo file: py.test -v test_foo::TestAlice::test_foo or py.test -v test_foo::TestAlice work fine. And to find out the node IDs one can use --collectonly. ``` Hope this helps. Best regards, Jurko Gospodneti? From commits-noreply at bitbucket.org Sun Apr 6 08:16:30 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 06 Apr 2014 06:16:30 -0000 Subject: [Pytest-commit] commit/pytest: 4 new changesets Message-ID: <20140406061630.29703.27749@app17.ash-private.bitbucket.org> 4 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/ecce84841be1/ Changeset: ecce84841be1 Branch: jurko/tox_usage User: jurko Date: 2014-04-05 08:47:04 Summary: fix runtox.py failure when 'tox' is not available on the current system path Now just assumes that the tox module is available in the executing Python environment. Affected #: 1 file diff -r 8b3cd3ae724fbaa527cba25af3243f6f872cb250 -r ecce84841be15e704a465a6378878b6bd09318ea runtox.py --- a/runtox.py +++ b/runtox.py @@ -4,7 +4,7 @@ import sys if __name__ == "__main__": - subprocess.call(["tox", + subprocess.call([sys.executable, "-m", "tox", "-i", "ALL=https://devpi.net/hpk/dev/", "--develop",] + sys.argv[1:]) https://bitbucket.org/hpk42/pytest/commits/a7c05f253edb/ Changeset: a7c05f253edb Branch: jurko/tox_usage User: jurko Date: 2014-04-05 08:49:30 Summary: stylistic runtox.py code cleanup Affected #: 1 file diff -r ecce84841be15e704a465a6378878b6bd09318ea -r a7c05f253edbccbd1a8593456d9c09d274a59a15 runtox.py --- a/runtox.py +++ b/runtox.py @@ -6,5 +6,4 @@ if __name__ == "__main__": subprocess.call([sys.executable, "-m", "tox", "-i", "ALL=https://devpi.net/hpk/dev/", - "--develop",] + sys.argv[1:]) - + "--develop"] + sys.argv[1:]) https://bitbucket.org/hpk42/pytest/commits/75fb943397d3/ Changeset: 75fb943397d3 Branch: jurko/tox_usage User: jurko Date: 2014-04-05 08:51:00 Summary: make runtox.py not import external modules unless run as a script Was not needed since the script actually does nothing unless run as a script. Affected #: 1 file diff -r a7c05f253edbccbd1a8593456d9c09d274a59a15 -r 75fb943397d382d39554f678bacc5abd9ff49b49 runtox.py --- a/runtox.py +++ b/runtox.py @@ -1,9 +1,8 @@ #!/usr/bin/env python -import subprocess -import sys - if __name__ == "__main__": + import subprocess + import sys subprocess.call([sys.executable, "-m", "tox", "-i", "ALL=https://devpi.net/hpk/dev/", "--develop"] + sys.argv[1:]) https://bitbucket.org/hpk42/pytest/commits/393981dc0f1d/ Changeset: 393981dc0f1d User: hpk42 Date: 2014-04-06 08:16:24 Summary: Merged in jurko/pytest/jurko/tox_usage (pull request #150) runtox.py cleanup Affected #: 1 file diff -r 8b3cd3ae724fbaa527cba25af3243f6f872cb250 -r 393981dc0f1da8a2699ed4c8bc43f3644af592f9 runtox.py --- a/runtox.py +++ b/runtox.py @@ -1,10 +1,8 @@ #!/usr/bin/env python -import subprocess -import sys - if __name__ == "__main__": - subprocess.call(["tox", + import subprocess + import sys + subprocess.call([sys.executable, "-m", "tox", "-i", "ALL=https://devpi.net/hpk/dev/", - "--develop",] + sys.argv[1:]) - + "--develop"] + sys.argv[1:]) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 7 13:44:19 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 07 Apr 2014 11:44:19 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue439: clarify that capsys/capfd capture output during Message-ID: <20140407114419.12683.37226@app10.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/c15664850085/ Changeset: c15664850085 User: hpk42 Date: 2014-04-07 13:42:48 Summary: fix issue439: clarify that capsys/capfd capture output during test execution, not test setup. Affected #: 1 file diff -r 393981dc0f1da8a2699ed4c8bc43f3644af592f9 -r c15664850085ac1b38d81b5fb3b30cd48c25f2db doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -84,11 +84,9 @@ Accessing captured output from a test function --------------------------------------------------- -The :ref:`funcarg mechanism` allows test function a very easy -way to access the captured output by simply using the names -``capsys`` or ``capfd`` in the test function signature. Here -is an example test function that performs some output related -checks:: +The ``capsys`` and ``capfd`` fixtures allow to access stdout/stderr +output created during test execution. Here is an example test function +that performs some output related checks:: def test_myoutput(capsys): # or use "capfd" for fd-level print ("hello") @@ -108,8 +106,10 @@ output streams and also interacts well with pytest's own per-test capturing. -If you want to capture on ``fd`` level you can use +If you want to capture on filedescriptor level you can use the ``capfd`` function argument which offers the exact -same interface. +same interface but allows to also capture output from +libraries or subprocesses that directly write to operating +system level output streams (FD1 and FD2). .. include:: links.inc Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 7 13:51:08 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 07 Apr 2014 11:51:08 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140407115108.24903.75037@app12.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/6a5904c4816c/ Changeset: 6a5904c4816c Branch: issue498 User: hpk42 Date: 2014-04-07 13:29:57 Summary: fix issue498: if a fixture finalizer fails, make sure that the fixture is still invalidated. Affected #: 3 files diff -r 393981dc0f1da8a2699ed4c8bc43f3644af592f9 -r 6a5904c4816cebd3e146a4277c0ad5021b131753 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue498: if a fixture finalizer fails, make sure that + the fixture is still invalidated. + - fix issue453: the result of the pytest_assertrepr_compare hook now gets it's newlines escaped so that format_exception does not blow up. diff -r 393981dc0f1da8a2699ed4c8bc43f3644af592f9 -r 6a5904c4816cebd3e146a4277c0ad5021b131753 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1778,13 +1778,15 @@ self._finalizer.append(finalizer) def finish(self): - while self._finalizer: - func = self._finalizer.pop() - func() try: - del self.cached_result - except AttributeError: - pass + while self._finalizer: + func = self._finalizer.pop() + func() + finally: + # even if finalization fails, we invalidate + # the cached fixture value + if hasattr(self, "cached_result"): + del self.cached_result def execute(self, request): # get required arguments and register our own finish() diff -r 393981dc0f1da8a2699ed4c8bc43f3644af592f9 -r 6a5904c4816cebd3e146a4277c0ad5021b131753 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -2087,6 +2087,35 @@ "*1 error*", ]) + def test_issue498_fixture_finalizer_failing(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture + def fix1(request): + def f(): + raise KeyError + request.addfinalizer(f) + return object() + + l = [] + def test_1(fix1): + l.append(fix1) + def test_2(fix1): + l.append(fix1) + def test_3(): + assert l[0] != l[1] + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *ERROR*teardown*test_1* + *KeyError* + *ERROR*teardown*test_2* + *KeyError* + *3 pass*2 error* + """) + + + def test_setupfunc_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest https://bitbucket.org/hpk42/pytest/commits/81ac392651f5/ Changeset: 81ac392651f5 User: RonnyPfannschmidt Date: 2014-04-07 13:51:03 Summary: Merged in hpk42/pytest-hpk/issue498 (pull request #151) fix issue498: if a fixture finalizer fails, make sure that the fixture Affected #: 3 files diff -r c15664850085ac1b38d81b5fb3b30cd48c25f2db -r 81ac392651f55934e99828e279c38990e76a873e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue498: if a fixture finalizer fails, make sure that + the fixture is still invalidated. + - fix issue453: the result of the pytest_assertrepr_compare hook now gets it's newlines escaped so that format_exception does not blow up. diff -r c15664850085ac1b38d81b5fb3b30cd48c25f2db -r 81ac392651f55934e99828e279c38990e76a873e _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1778,13 +1778,15 @@ self._finalizer.append(finalizer) def finish(self): - while self._finalizer: - func = self._finalizer.pop() - func() try: - del self.cached_result - except AttributeError: - pass + while self._finalizer: + func = self._finalizer.pop() + func() + finally: + # even if finalization fails, we invalidate + # the cached fixture value + if hasattr(self, "cached_result"): + del self.cached_result def execute(self, request): # get required arguments and register our own finish() diff -r c15664850085ac1b38d81b5fb3b30cd48c25f2db -r 81ac392651f55934e99828e279c38990e76a873e testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -2087,6 +2087,35 @@ "*1 error*", ]) + def test_issue498_fixture_finalizer_failing(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture + def fix1(request): + def f(): + raise KeyError + request.addfinalizer(f) + return object() + + l = [] + def test_1(fix1): + l.append(fix1) + def test_2(fix1): + l.append(fix1) + def test_3(): + assert l[0] != l[1] + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *ERROR*teardown*test_1* + *KeyError* + *ERROR*teardown*test_2* + *KeyError* + *3 pass*2 error* + """) + + + def test_setupfunc_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 9 06:46:05 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 09 Apr 2014 04:46:05 -0000 Subject: [Pytest-commit] commit/pytest: RonnyPfannschmidt: Merged in hpk42/pytest-hpk/issue473 (pull request #152) Message-ID: <20140409044605.27495.58502@app13.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/7b32e3807b23/ Changeset: 7b32e3807b23 User: RonnyPfannschmidt Date: 2014-04-09 06:46:01 Summary: Merged in hpk42/pytest-hpk/issue473 (pull request #152) fix issue473: work around mock putting an unbound method into a class Affected #: 3 files diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue473: work around mock putting an unbound method into a class + dict when double-patching. + - fix issue498: if a fixture finalizer fails, make sure that the fixture is still invalidated. diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -222,6 +222,8 @@ return Class(name, parent=collector) elif collector.funcnamefilter(name) and hasattr(obj, "__call__") and \ getfixturemarker(obj) is None: + # mock seems to store unbound methods (issue473), let's normalize it + obj = getattr(obj, "__func__", obj) if not isfunction(obj): collector.warn(code="C2", message= "cannot collect %r because it is not a function." diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -164,6 +164,21 @@ names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] + def test_mock_double_patch_issue473(self, testdir): + testdir.makepyfile(""" + from mock import patch + from pytest import mark + + @patch('os.getcwd') + @patch('os.path') + @mark.slow + class TestSimple: + def test_simple_thing(self, mock_path, mock_getcwd): + pass + """) + res = testdir.inline_run() + res.assertoutcome(passed=1) + class TestReRunTests: def test_rerun(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 9 06:46:05 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 09 Apr 2014 04:46:05 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140409044605.29257.21483@app09.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/7a7fe6968913/ Changeset: 7a7fe6968913 Branch: issue473 User: hpk42 Date: 2014-04-08 12:50:13 Summary: fix issue473: work around mock putting an unbound method into a class dict when double-patching. Affected #: 3 files diff -r 6a5904c4816cebd3e146a4277c0ad5021b131753 -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue473: work around mock putting an unbound method into a class + dict when double-patching. + - fix issue498: if a fixture finalizer fails, make sure that the fixture is still invalidated. diff -r 6a5904c4816cebd3e146a4277c0ad5021b131753 -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -222,6 +222,8 @@ return Class(name, parent=collector) elif collector.funcnamefilter(name) and hasattr(obj, "__call__") and \ getfixturemarker(obj) is None: + # mock seems to store unbound methods (issue473), let's normalize it + obj = getattr(obj, "__func__", obj) if not isfunction(obj): collector.warn(code="C2", message= "cannot collect %r because it is not a function." diff -r 6a5904c4816cebd3e146a4277c0ad5021b131753 -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -164,6 +164,21 @@ names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] + def test_mock_double_patch_issue473(self, testdir): + testdir.makepyfile(""" + from mock import patch + from pytest import mark + + @patch('os.getcwd') + @patch('os.path') + @mark.slow + class TestSimple: + def test_simple_thing(self, mock_path, mock_getcwd): + pass + """) + res = testdir.inline_run() + res.assertoutcome(passed=1) + class TestReRunTests: def test_rerun(self, testdir): https://bitbucket.org/hpk42/pytest/commits/7b32e3807b23/ Changeset: 7b32e3807b23 User: RonnyPfannschmidt Date: 2014-04-09 06:46:01 Summary: Merged in hpk42/pytest-hpk/issue473 (pull request #152) fix issue473: work around mock putting an unbound method into a class Affected #: 3 files diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue473: work around mock putting an unbound method into a class + dict when double-patching. + - fix issue498: if a fixture finalizer fails, make sure that the fixture is still invalidated. diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -222,6 +222,8 @@ return Class(name, parent=collector) elif collector.funcnamefilter(name) and hasattr(obj, "__call__") and \ getfixturemarker(obj) is None: + # mock seems to store unbound methods (issue473), let's normalize it + obj = getattr(obj, "__func__", obj) if not isfunction(obj): collector.warn(code="C2", message= "cannot collect %r because it is not a function." diff -r 81ac392651f55934e99828e279c38990e76a873e -r 7b32e3807b231ba322b86223668d805bdacca464 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -164,6 +164,21 @@ names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] + def test_mock_double_patch_issue473(self, testdir): + testdir.makepyfile(""" + from mock import patch + from pytest import mark + + @patch('os.getcwd') + @patch('os.path') + @mark.slow + class TestSimple: + def test_simple_thing(self, mock_path, mock_getcwd): + pass + """) + res = testdir.inline_run() + res.assertoutcome(passed=1) + class TestReRunTests: def test_rerun(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Apr 9 06:46:06 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 09 Apr 2014 04:46:06 -0000 Subject: [Pytest-commit] commit/pytest: RonnyPfannschmidt: Close branch issue473 Message-ID: <20140409044606.1867.27436@app02.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/d732a47ccc6e/ Changeset: d732a47ccc6e Branch: issue473 User: RonnyPfannschmidt Date: 2014-04-09 06:46:01 Summary: Close branch issue473 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Thu Apr 10 11:39:50 2014 From: issues-reply at bitbucket.org (=?utf-8?q?Thomas_G=C3=BCttler?=) Date: Thu, 10 Apr 2014 09:39:50 -0000 Subject: [Pytest-commit] Issue #500: Warn if `py.test -k foo` finds no tests. (hpk42/pytest) Message-ID: <20140410093950.23997.58976@app12.ash-private.bitbucket.org> New issue 500: Warn if `py.test -k foo` finds no tests. https://bitbucket.org/hpk42/pytest/issue/500/warn-if-pytest-k-foo-finds-no-tests Thomas G?ttler: I think it would be a good useability improvement, if py.test would warn me, if `py.test -k foo` finds no test at all. I use version 2.5.2 and I see a green line, which indicates that all is good. But if no tests was found, at least for me (interactive usage), it is a fault. It would be nice to see a different color (not green) if no test was selected. From commits-noreply at bitbucket.org Thu Apr 10 22:38:57 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 10 Apr 2014 20:38:57 -0000 Subject: [Pytest-commit] commit/pytest: bubenkoff: Merged in hpk42/pytest-hpk/nose_test_attr (pull request #154) Message-ID: <20140410203857.31530.31044@app02.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/912a8874de47/ Changeset: 912a8874de47 User: bubenkoff Date: 2014-04-10 22:38:53 Summary: Merged in hpk42/pytest-hpk/nose_test_attr (pull request #154) support nose-style __test__ attribute to disable collection of test modules/classes/functions Affected #: 5 files diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -64,6 +64,10 @@ - fix issue443: fix skip examples to use proper comparison. Thanks Alex Groenholm. +- support nose-style ``__test__`` attribute on modules, classes and + functions, including unittest-style Classes. If set to False, the + test will not be collected. + 2.5.2 ----------------------------------- diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -229,10 +229,11 @@ "cannot collect %r because it is not a function." % name, ) return - if is_generator(obj): - return Generator(name, parent=collector) - else: - return list(collector._genfunctions(name, obj)) + if getattr(obj, "__test__", True): + if is_generator(obj): + return Generator(name, parent=collector) + else: + return list(collector._genfunctions(name, obj)) def is_generator(func): try: @@ -314,6 +315,9 @@ return True def collect(self): + if not getattr(self.obj, "__test__", True): + return [] + # NB. we avoid random getattrs and peek in the __dict__ instead # (XXX originally introduced from a PyPy need, still true?) dicts = [getattr(self.obj, '__dict__', {})] diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -41,10 +41,12 @@ super(UnitTestCase, self).setup() def collect(self): + cls = self.obj + if not getattr(cls, "__test__", True): + return self.session._fixturemanager.parsefactories(self, unittest=True) loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj - cls = self.obj foundsomething = False for name in loader.getTestCaseNames(self.obj): x = getattr(self.obj, name) diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 doc/en/nose.txt --- a/doc/en/nose.txt +++ b/doc/en/nose.txt @@ -25,6 +25,7 @@ * SkipTest exceptions and markers * setup/teardown decorators * yield-based tests and their setup +* ``__test__`` attribute on modules/classes/functions * general usage of nose utilities Unsupported idioms / known issues diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -165,6 +165,7 @@ assert names == ["test_one", "test_two", "test_three"] def test_mock_double_patch_issue473(self, testdir): + pytest.importorskip("mock", "1.0.1") testdir.makepyfile(""" from mock import patch from pytest import mark @@ -176,8 +177,8 @@ def test_simple_thing(self, mock_path, mock_getcwd): pass """) - res = testdir.inline_run() - res.assertoutcome(passed=1) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) class TestReRunTests: @@ -214,3 +215,52 @@ def test_pytestconfig_is_session_scoped(): from _pytest.python import pytestconfig assert pytestconfig._pytestfixturefunction.scope == "session" + + +class TestNoselikeTestAttribute: + def test_module(self, testdir): + testdir.makepyfile(""" + __test__ = False + def test_hello(): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_class_and_method(self, testdir): + testdir.makepyfile(""" + __test__ = True + def test_func(): + pass + test_func.__test__ = False + + class TestSome: + __test__ = False + def test_method(self): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_unittest_class(self, testdir): + testdir.makepyfile(""" + import unittest + class TC(unittest.TestCase): + def test_1(self): + pass + class TC2(unittest.TestCase): + __test__ = False + def test_2(self): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + call = reprec.getcalls("pytest_collection_modifyitems")[0] + assert len(call.items) == 1 + assert call.items[0].cls.__name__ == "TC" + + Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 10 22:38:57 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 10 Apr 2014 20:38:57 -0000 Subject: [Pytest-commit] commit/pytest: 6 new changesets Message-ID: <20140410203857.8983.15485@app04.ash-private.bitbucket.org> 6 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/8690ae5a5508/ Changeset: 8690ae5a5508 Branch: nose_test_attr User: hpk42 Date: 2014-04-10 12:46:27 Summary: support nose-style ``__test__`` attribute on modules, classes and functions, including unittest-style Classes. If set to True, the test will not be collected. Affected #: 5 files diff -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -64,6 +64,10 @@ - fix issue443: fix skip examples to use proper comparison. Thanks Alex Groenholm. +- support nose-style ``__test__`` attribute on modules, classes and + functions, including unittest-style Classes. If set to True, the + test will not be collected. + 2.5.2 ----------------------------------- diff -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -314,6 +314,9 @@ return True def collect(self): + if not getattr(self.obj, "__test__", True): + return [] + # NB. we avoid random getattrs and peek in the __dict__ instead # (XXX originally introduced from a PyPy need, still true?) dicts = [getattr(self.obj, '__dict__', {})] diff -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -41,10 +41,12 @@ super(UnitTestCase, self).setup() def collect(self): + cls = self.obj + if not getattr(cls, "__test__", True): + return self.session._fixturemanager.parsefactories(self, unittest=True) loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj - cls = self.obj foundsomething = False for name in loader.getTestCaseNames(self.obj): x = getattr(self.obj, name) diff -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 doc/en/nose.txt --- a/doc/en/nose.txt +++ b/doc/en/nose.txt @@ -25,6 +25,7 @@ * SkipTest exceptions and markers * setup/teardown decorators * yield-based tests and their setup +* ``__test__`` attribute on modules/classes/functions * general usage of nose utilities Unsupported idioms / known issues diff -r 7a7fe696891309a4e2d9b03001972c6362ffd0ff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -164,20 +164,21 @@ names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] - def test_mock_double_patch_issue473(self, testdir): + def test_mock_and_mark_issue473(self, testdir): + pytest.importorskip("mock", "1.0.1") testdir.makepyfile(""" from mock import patch from pytest import mark @patch('os.getcwd') @patch('os.path') - @mark.slow + #@mark.slow class TestSimple: def test_simple_thing(self, mock_path, mock_getcwd): pass """) - res = testdir.inline_run() - res.assertoutcome(passed=1) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) class TestReRunTests: @@ -214,3 +215,49 @@ def test_pytestconfig_is_session_scoped(): from _pytest.python import pytestconfig assert pytestconfig._pytestfixturefunction.scope == "session" + + +class TestNoselikeTestAttribute: + def test_module(self, testdir): + testdir.makepyfile(""" + __test__ = False + def test_hello(): + pass + """) + reprec = testdir.inline_run() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_class_and_method(self, testdir): + testdir.makepyfile(""" + __test__ = True + def test_func(): + pass + test_hello.__test__ = False + + class TestSome: + __test__ = False + def test_method(self): + pass + """) + reprec = testdir.inline_run() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_unittest_class(self, testdir): + testdir.makepyfile(""" + import unittest + class TC(unittest.TestCase): + def test_1(self): + pass + class TC2(unittest.TestCase): + __test__ = False + def test_2(self): + pass + """) + reprec = testdir.inline_run() + call = reprec.getcalls("pytest_collection_modifyitems")[0] + assert len(call.items) == 1 + assert call.items[0].cls.__name__ == "TC" + + https://bitbucket.org/hpk42/pytest/commits/588d2bf14ebf/ Changeset: 588d2bf14ebf Branch: nose_test_attr User: hpk42 Date: 2014-04-10 12:53:33 Summary: merge default Affected #: 1 file diff -r 8690ae5a550883558b7548fe1ffbaadb64f2e948 -r 588d2bf14ebf93f83af2be5e64d7dd3ac45722d2 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -84,11 +84,9 @@ Accessing captured output from a test function --------------------------------------------------- -The :ref:`funcarg mechanism` allows test function a very easy -way to access the captured output by simply using the names -``capsys`` or ``capfd`` in the test function signature. Here -is an example test function that performs some output related -checks:: +The ``capsys`` and ``capfd`` fixtures allow to access stdout/stderr +output created during test execution. Here is an example test function +that performs some output related checks:: def test_myoutput(capsys): # or use "capfd" for fd-level print ("hello") @@ -108,8 +106,10 @@ output streams and also interacts well with pytest's own per-test capturing. -If you want to capture on ``fd`` level you can use +If you want to capture on filedescriptor level you can use the ``capfd`` function argument which offers the exact -same interface. +same interface but allows to also capture output from +libraries or subprocesses that directly write to operating +system level output streams (FD1 and FD2). .. include:: links.inc https://bitbucket.org/hpk42/pytest/commits/b397dcb8966b/ Changeset: b397dcb8966b Branch: nose_test_attr User: hpk42 Date: 2014-04-10 12:56:14 Summary: fix typo in changelog Affected #: 1 file diff -r 588d2bf14ebf93f83af2be5e64d7dd3ac45722d2 -r b397dcb8966ba997ad72e007d3c8d497d5e7ec4a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -65,7 +65,7 @@ Groenholm. - support nose-style ``__test__`` attribute on modules, classes and - functions, including unittest-style Classes. If set to True, the + functions, including unittest-style Classes. If set to False, the test will not be collected. https://bitbucket.org/hpk42/pytest/commits/13983484d958/ Changeset: 13983484d958 Branch: nose_test_attr User: hpk42 Date: 2014-04-10 12:58:10 Summary: fix wrong merge Affected #: 1 file diff -r b397dcb8966ba997ad72e007d3c8d497d5e7ec4a -r 13983484d95880627faff1f7b9ff518c2d552f0a testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -164,7 +164,7 @@ names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] - def test_mock_and_mark_issue473(self, testdir): + def test_mock_double_patch_issue473(self, testdir): pytest.importorskip("mock", "1.0.1") testdir.makepyfile(""" from mock import patch @@ -172,7 +172,7 @@ @patch('os.getcwd') @patch('os.path') - #@mark.slow + @mark.slow class TestSimple: def test_simple_thing(self, mock_path, mock_getcwd): pass https://bitbucket.org/hpk42/pytest/commits/4535cc997cfb/ Changeset: 4535cc997cfb Branch: nose_test_attr User: hpk42 Date: 2014-04-10 13:37:39 Summary: fix tests to properly fail on failed collectiosn (which was hiding an error) and also implement __test__=False for test functions properly. Affected #: 2 files diff -r 13983484d95880627faff1f7b9ff518c2d552f0a -r 4535cc997cfb8d82327f5d17ad1dc855ee7df01d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -229,10 +229,11 @@ "cannot collect %r because it is not a function." % name, ) return - if is_generator(obj): - return Generator(name, parent=collector) - else: - return list(collector._genfunctions(name, obj)) + if getattr(obj, "__test__", True): + if is_generator(obj): + return Generator(name, parent=collector) + else: + return list(collector._genfunctions(name, obj)) def is_generator(func): try: diff -r 13983484d95880627faff1f7b9ff518c2d552f0a -r 4535cc997cfb8d82327f5d17ad1dc855ee7df01d testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -225,6 +225,7 @@ pass """) reprec = testdir.inline_run() + assert not reprec.getfailedcollections() calls = reprec.getreports("pytest_runtest_logreport") assert not calls @@ -233,7 +234,7 @@ __test__ = True def test_func(): pass - test_hello.__test__ = False + test_func.__test__ = False class TestSome: __test__ = False @@ -241,6 +242,7 @@ pass """) reprec = testdir.inline_run() + assert not reprec.getfailedcollections() calls = reprec.getreports("pytest_runtest_logreport") assert not calls @@ -256,6 +258,7 @@ pass """) reprec = testdir.inline_run() + assert not reprec.getfailedcollections() call = reprec.getcalls("pytest_collection_modifyitems")[0] assert len(call.items) == 1 assert call.items[0].cls.__name__ == "TC" https://bitbucket.org/hpk42/pytest/commits/912a8874de47/ Changeset: 912a8874de47 User: bubenkoff Date: 2014-04-10 22:38:53 Summary: Merged in hpk42/pytest-hpk/nose_test_attr (pull request #154) support nose-style __test__ attribute to disable collection of test modules/classes/functions Affected #: 5 files diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -64,6 +64,10 @@ - fix issue443: fix skip examples to use proper comparison. Thanks Alex Groenholm. +- support nose-style ``__test__`` attribute on modules, classes and + functions, including unittest-style Classes. If set to False, the + test will not be collected. + 2.5.2 ----------------------------------- diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -229,10 +229,11 @@ "cannot collect %r because it is not a function." % name, ) return - if is_generator(obj): - return Generator(name, parent=collector) - else: - return list(collector._genfunctions(name, obj)) + if getattr(obj, "__test__", True): + if is_generator(obj): + return Generator(name, parent=collector) + else: + return list(collector._genfunctions(name, obj)) def is_generator(func): try: @@ -314,6 +315,9 @@ return True def collect(self): + if not getattr(self.obj, "__test__", True): + return [] + # NB. we avoid random getattrs and peek in the __dict__ instead # (XXX originally introduced from a PyPy need, still true?) dicts = [getattr(self.obj, '__dict__', {})] diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -41,10 +41,12 @@ super(UnitTestCase, self).setup() def collect(self): + cls = self.obj + if not getattr(cls, "__test__", True): + return self.session._fixturemanager.parsefactories(self, unittest=True) loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj - cls = self.obj foundsomething = False for name in loader.getTestCaseNames(self.obj): x = getattr(self.obj, name) diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 doc/en/nose.txt --- a/doc/en/nose.txt +++ b/doc/en/nose.txt @@ -25,6 +25,7 @@ * SkipTest exceptions and markers * setup/teardown decorators * yield-based tests and their setup +* ``__test__`` attribute on modules/classes/functions * general usage of nose utilities Unsupported idioms / known issues diff -r 7b32e3807b231ba322b86223668d805bdacca464 -r 912a8874de477546929a62539680be3e2322e042 testing/python/integration.py --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -165,6 +165,7 @@ assert names == ["test_one", "test_two", "test_three"] def test_mock_double_patch_issue473(self, testdir): + pytest.importorskip("mock", "1.0.1") testdir.makepyfile(""" from mock import patch from pytest import mark @@ -176,8 +177,8 @@ def test_simple_thing(self, mock_path, mock_getcwd): pass """) - res = testdir.inline_run() - res.assertoutcome(passed=1) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) class TestReRunTests: @@ -214,3 +215,52 @@ def test_pytestconfig_is_session_scoped(): from _pytest.python import pytestconfig assert pytestconfig._pytestfixturefunction.scope == "session" + + +class TestNoselikeTestAttribute: + def test_module(self, testdir): + testdir.makepyfile(""" + __test__ = False + def test_hello(): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_class_and_method(self, testdir): + testdir.makepyfile(""" + __test__ = True + def test_func(): + pass + test_func.__test__ = False + + class TestSome: + __test__ = False + def test_method(self): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + calls = reprec.getreports("pytest_runtest_logreport") + assert not calls + + def test_unittest_class(self, testdir): + testdir.makepyfile(""" + import unittest + class TC(unittest.TestCase): + def test_1(self): + pass + class TC2(unittest.TestCase): + __test__ = False + def test_2(self): + pass + """) + reprec = testdir.inline_run() + assert not reprec.getfailedcollections() + call = reprec.getcalls("pytest_collection_modifyitems")[0] + assert len(call.items) == 1 + assert call.items[0].cls.__name__ == "TC" + + Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Apr 10 22:38:59 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 10 Apr 2014 20:38:59 -0000 Subject: [Pytest-commit] commit/pytest: bubenkoff: Close branch nose_test_attr Message-ID: <20140410203859.26890.41424@app15.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/1bbe10ccc558/ Changeset: 1bbe10ccc558 Branch: nose_test_attr User: bubenkoff Date: 2014-04-10 22:38:53 Summary: Close branch nose_test_attr Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Fri Apr 11 17:00:13 2014 From: issues-reply at bitbucket.org (Andreas Pelme) Date: Fri, 11 Apr 2014 15:00:13 -0000 Subject: [Pytest-commit] Issue #501: xdist process crashes with ImportError (hpk42/pytest) Message-ID: <20140411150013.20673.24813@app06.ash-private.bitbucket.org> New issue 501: xdist process crashes with ImportError https://bitbucket.org/hpk42/pytest/issue/501/xdist-process-crashes-with-importerror Andreas Pelme: I encountered this issue when originally trying to debug another issue. I might have something screwed up on my machine, but I cannot figure out what it is, and any pointers would be helpful. ``` ~ $ mkvirtualenv tmp1234 New python executable in tmp1234/bin/python Installing setuptools, pip...done. [tmp1234] ~ $ cd code/pytest [tmp1234] ~/code/pytest $ hg log|head -n1 changeset: 3585:1bbe10ccc558 [tmp1234] ~/code/pytest $ pip install -e . Obtaining file:///Users/andreas/code/pytest Running setup.py (path:/Users/andreas/code/pytest/setup.py) egg_info for package from file:///Users/andreas/code/pytest Downloading/unpacking py>=1.4.19 (from pytest==2.5.2.dev1) http://localhost:3141/root/pypi/+simple/py/ uses an insecure transport scheme (http). Consider using https if localhost:3141 has it available Downloading py-1.4.20.tar.gz (185kB): 185kB downloaded Running setup.py (path:/Users/andreas/.virtualenvs/tmp1234/build/py/setup.py) egg_info for package py Installing collected packages: py, pytest Running setup.py install for py Running setup.py develop for pytest Creating /Users/andreas/.virtualenvs/tmp1234/lib/python2.7/site-packages/pytest.egg-link (link to .) Adding pytest 2.5.2.dev1 to easy-install.pth file Installing py.test script to /Users/andreas/.virtualenvs/tmp1234/bin Installing py.test-2.7 script to /Users/andreas/.virtualenvs/tmp1234/bin Installed /Users/andreas/code/pytest Successfully installed py pytest Cleaning up... [tmp1234] ~/code/pytest $ cd ~/code/pytest-xdist [tmp1234] ~/code/pytest-xdist $ hg log|head -n1 changeset: 174:81566ffbb271 [tmp1234] ~/code/pytest-xdist $ pip install -e . Obtaining file:///Users/andreas/code/pytest-xdist Running setup.py (path:/Users/andreas/code/pytest-xdist/setup.py) egg_info for package from file:///Users/andreas/code/pytest-xdist no previously-included directories found matching '.hg' Downloading/unpacking execnet>=1.1 (from pytest-xdist==1.11.dev1) http://localhost:3141/root/pypi/+simple/execnet/ uses an insecure transport scheme (http). Consider using https if localhost:3141 has it available Downloading execnet-1.2.0.tar.gz (163kB): 163kB downloaded Running setup.py (path:/Users/andreas/.virtualenvs/tmp1234/build/execnet/setup.py) egg_info for package execnet warning: no files found matching 'conftest.py' Requirement already satisfied (use --upgrade to upgrade): pytest>=2.4.2 in /Users/andreas/code/pytest (from pytest-xdist==1.11.dev1) Requirement already satisfied (use --upgrade to upgrade): py>=1.4.19 in /Users/andreas/.virtualenvs/tmp1234/lib/python2.7/site-packages (from pytest>=2.4.2->pytest-xdist==1.11.dev1) Installing collected packages: execnet, pytest-xdist Running setup.py install for execnet warning: no files found matching 'conftest.py' Running setup.py develop for pytest-xdist no previously-included directories found matching '.hg' Creating /Users/andreas/.virtualenvs/tmp1234/lib/python2.7/site-packages/pytest-xdist.egg-link (link to .) Adding pytest-xdist 1.11.dev1 to easy-install.pth file Installed /Users/andreas/code/pytest-xdist Successfully installed execnet pytest-xdist Cleaning up... ``` Things seems to be installed properly: ``` [tmp1234] ~ $ python Python 2.7.5 (default, Sep 12 2013, 21:33:34) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import pytest;pytest >>> import _pytest.config;_pytest.config >>> import xdist;xdist >>> [tmp1234] ~ $ py.test --help usage: py.test [options] [file_or_dir] [file_or_dir] [...] positional arguments: file_or_dir general: -k EXPRESSION only run tests which match the given substring expression. An expression is a python evaluatable expression where all names are substring-matched against test names and their parent classes. Example: -k 'test_method or test other' matches all test functions and classes whose name contains 'test_method' or 'test_other'. Additionally keywords are matched to classes and functions containing extra names in their 'extra_keyword_matches' set, as well as functions which have names assigned directly to them. -m MARKEXPR only run tests matching given mark expression. example: -m 'mark1 and not mark2'. --markers show markers (builtin, plugin and per-project ones). -x, --exitfirst exit instantly on first error or failed test. --maxfail num exit after first num failures or errors. --strict run pytest in strict mode, warnings become errors. --fixtures, --funcargs show available fixtures, sorted by plugin appearance --pdb start the interactive Python debugger on errors. --capture method per-test capturing method: one of fd (default)|sys|no. -s shortcut for --capture=no. --runxfail run tests even if they are marked xfail reporting: -v, --verbose increase verbosity. -q, --quiet decrease verbosity. -r chars show extra test summary info as specified by chars (f)ailed, (E)error, (s)skipped, (x)failed, (X)passed. -l, --showlocals show locals in tracebacks (disabled by default). --report opts (deprecated, use -r) --tb style traceback print mode (long/short/line/native/no). --full-trace don't cut any tracebacks (default is to cut). --color color color terminal output (yes/no/auto). --durations N show N slowest setup/test durations (N=0 for all). --pastebin mode send failed|all info to bpaste.net pastebin service. --junit-xml path create junit-xml style report file at given path. --junit-prefix str prepend prefix to classnames in junit-xml output --result-log path path for machine-readable result log. collection: --collect-only only collect tests, don't execute them. --pyargs try to interpret all arguments as python packages. --ignore path ignore path during collection (multi-allowed). --confcutdir dir only load conftest.py's relative to specified dir. --doctest-modules run doctests in all .py modules --doctest-glob pat doctests file matching pattern, default: test*.txt test session debugging and configuration: --basetemp dir base temporary directory for this test run. --version display pytest lib version and import information. -h, --help show help message and configuration info -p name early-load given plugin (multi-allowed). To avoid loading of plugins, use the `no:` prefix, e.g. `no:doctest`. --trace-config trace considerations of conftest.py files. --debug store internal tracing debug information in 'pytestdebug.log'. --assert MODE control assertion debugging tools. 'plain' performs no assertion debugging. 'reinterp' reinterprets assert statements after they failed to provide assertion expression information. 'rewrite' (the default) rewrites assert statements in test modules on import to provide assert expression information. --no-assert DEPRECATED equivalent to --assert=plain --no-magic DEPRECATED equivalent to --assert=plain --genscript path create standalone pytest script at given target path. distributed and subprocess testing: -f, --looponfail run tests in subprocess, wait for modified files and re-run failing test set until all pass. -n numprocesses shortcut for '--dist=load --tx=NUM*popen' --boxed box each test run in a separate process (unix) --dist distmode set mode for distributing tests to exec environments. each: send each test to each available environment. load: send each test to available environment. (default) no: run tests inprocess, don't distribute. --tx xspec add a test execution environment. some examples: --tx popen//python=python2.5 --tx socket=192.168.1.102:8888 --tx ssh=user at codespeak.net//chdir=testcache -d load-balance tests. shortcut for '--dist=load' --rsyncdir DIR add directory for rsyncing to remote tx nodes. --rsyncignore GLOB add expression for ignores when rsyncing to remote tx nodes. [pytest] ini-options in the next pytest.ini|tox.ini|setup.cfg file: markers (linelist) markers for test functions norecursedirs (args) directory patterns to avoid for recursion usefixtures (args) list of default fixtures to be used with this project python_files (args) glob-style file patterns for Python test module disco python_classes (args) prefixes for Python test class discovery python_functions (args) prefixes for Python test function and method discover addopts (args) extra command line options minversion (string) minimally required pytest version rsyncdirs (pathlist) list of (relative) paths to be rsynced for remote dis rsyncignore (pathlist) list of (relative) glob-style paths to be ignored for looponfailroots (pathlist) directories to check for changes to see available markers type: py.test --markers to see available fixtures type: py.test --fixtures ``` However, any invocations of py.test with xdist crashes like this: ``` [tmp1234] ~ $ py.test -n1 ============================= test session starts ============================== platform darwin -- Python 2.7.5 -- pytest-2.5.2.dev1 plugins: xdist gw0 C[gw0] node down: Traceback (most recent call last): File "/Users/andreas/.virtualenvs/tmp1234/lib/python2.7/site-packages/execnet/gateway_base.py", line 1029, in executetask do_exec(co, loc) # noqa File "", line 1, in do_exec File "", line 139, in File "", line 115, in remote_initconfig File "/Users/andreas/code/pytest/_pytest/config.py", line 4, in import pytest File "/Users/andreas/code/pytest/pytest.py", line 14, in from _pytest.config import main, UsageError, _preloadplugins, cmdline ImportError: cannot import name main =============================== in 0.03 seconds =============================== ``` From issues-reply at bitbucket.org Sat Apr 12 06:39:20 2014 From: issues-reply at bitbucket.org (Justin Abrahms) Date: Sat, 12 Apr 2014 04:39:20 -0000 Subject: [Pytest-commit] Issue #165: NoInterpreterInfo confused as missing version (hpk42/tox) Message-ID: <20140412043920.26420.2032@app02.ash-private.bitbucket.org> New issue 165: NoInterpreterInfo confused as missing version https://bitbucket.org/hpk42/tox/issue/165/nointerpreterinfo-confused-as-missing Justin Abrahms: If have `PYTHONPATH` set, tests for py34 fail with an error indicating that 2.5 is not a supported version. Had to drop into a debug session to figure this one out. Relevant code snippet below. ``` 181 def getsupportedinterpreter(self): 182 import pdb; pdb.set_trace() 183 if sys.platform == "win32" and self.basepython and \ 184 "jython" in self.basepython: 185 raise tox.exception.UnsupportedInterpreter( 186 "Jython/Windows does not support installing scripts") 187 info = self.config.interpreters.get_info(self.basepython) 188 if not info.executable: 189 raise tox.exception.InterpreterNotFound(self.basepython) 190 if info.version_info < (2,6): 191 -> raise tox.exception.UnsupportedInterpreter( 192 "python2.5 is not supported anymore, sorry") 193 return info.executable (Pdb++) info (Pdb++) pp info.__dict__ {'err': 'Failed to import the site module\nYour PYTHONPATH points to a site-packages dir for Python 2.x but you are running Python 3.x!\n PYTHONPATH is currently: "/usr/local/lib/python2.7/site-packages:"\n You should `unset PYTHONPATH` to fix this.\n', 'executable': local('/usr/local/bin/python3.4'), 'name': 'python3.4', 'out': '', 'version_info': None} ``` From issues-reply at bitbucket.org Sat Apr 12 14:30:48 2014 From: issues-reply at bitbucket.org (offbyone) Date: Sat, 12 Apr 2014 12:30:48 -0000 Subject: [Pytest-commit] Issue #502: py.test --doctest-modules doesn't skip setup.py (hpk42/pytest) Message-ID: <20140412123048.16351.9688@app09.ash-private.bitbucket.org> New issue 502: py.test --doctest-modules doesn't skip setup.py https://bitbucket.org/hpk42/pytest/issue/502/pytest-doctest-modules-doesnt-skip-setuppy offbyone: If I have the following path layout: /setup.py /package_name/ /package_name/__init__.py I expect `--doctest-modules` not to read in setup.py. I can control it with conftest.py, but that seems a bit iffy. A better way might be to (IF the project is a setuptools package) take advantage of the setuptools project declaration to find the set of modules under test there. In any case, `setup.py` should be skipped. From commits-noreply at bitbucket.org Sat Apr 12 17:28:27 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sat, 12 Apr 2014 15:28:27 -0000 Subject: [Pytest-commit] commit/pytest: flub: Change XPASS colour to be yellow rather then red Message-ID: <20140412152827.17918.75296@app06.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/69395c44408c/ Changeset: 69395c44408c User: flub Date: 2014-04-12 16:27:12 Summary: Change XPASS colour to be yellow rather then red Unfortunately I'm not sure how to test this. Affected #: 1 file diff -r 912a8874de477546929a62539680be3e2322e042 -r 69395c44408cf30b21e7ed295a977130f46859b4 _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -186,7 +186,7 @@ if report.skipped: return "xfailed", "x", "xfail" elif report.failed: - return "xpassed", "X", "XPASS" + return "xpassed", "X", ("XPASS", {'yellow': True}) # called by the terminalreporter instance/plugin def pytest_terminal_summary(terminalreporter): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Sat Apr 12 19:04:00 2014 From: issues-reply at bitbucket.org (Andreas Pelme) Date: Sat, 12 Apr 2014 17:04:00 -0000 Subject: [Pytest-commit] Issue #503: Session fixtures invoked (hpk42/pytest) Message-ID: <20140412170400.10852.46823@app06.ash-private.bitbucket.org> New issue 503: Session fixtures invoked https://bitbucket.org/hpk42/pytest/issue/503/session-fixtures-invoked Andreas Pelme: Since https://bitbucket.org/hpk42/pytest-xdist/commits/05aa4f48306c905ae4d183a4b337f63cc4490466 session scoped fixtures runs multiple times. To trace this, I have added this code to the problematic fixture: ``` class Count(object): counter = 0 counter = Count() @pytest.fixture(scope='session') def _django_db_setup(request, _django_runner, _django_cursor_wrapper): """Session-wide database setup, internal to pytest-django""" counter.counter += 1 if counter.counter >= 2: print '_django_db_setup called %d times!' % counter.counter assert 0 # ... no other changes ``` This is what happends: ``` $ py.test functional_tests/test_pdf_generation.py --capture=fd -v -n1 ======================================================== test session starts ======================================================== platform darwin -- Python 2.7.5 -- py-1.4.21.dev1 -- pytest-2.6.0.dev1 plugins: django, xdist, cache, instafail gw0 [3] scheduling tests via LoadScheduling .E _________________________________________ ERROR at setup of test_kontrolluppgift_pdf_staff __________________________________________ [gw0] darwin -- Python 2.7.5 /Users/andreas/code/personalkollen/venv-2.7/bin/python ../pytest_django/pytest_django/plugin.py:177: in _django_db_marker > request.getfuncargvalue('db') ../pytest/_pytest/python.py:1319: in getfuncargvalue > return self._get_active_fixturedef(argname).cached_result[0] ../pytest/_pytest/python.py:1333: in _get_active_fixturedef > result = self._getfuncargvalue(fixturedef) ../pytest/_pytest/python.py:1385: in _getfuncargvalue > val = fixturedef.execute(request=subrequest) ../pytest/_pytest/python.py:1802: in execute > fixturedef = request._get_active_fixturedef(argname) ../pytest/_pytest/python.py:1333: in _get_active_fixturedef > result = self._getfuncargvalue(fixturedef) ../pytest/_pytest/python.py:1385: in _getfuncargvalue > val = fixturedef.execute(request=subrequest) ../pytest/_pytest/python.py:1835: in execute > self.yieldctx) ../pytest/_pytest/python.py:1761: in call_fixture_func > res = fixturefunc(**kwargs) ../pytest_django/pytest_django/fixtures.py:32: in _django_db_setup > assert 0 E assert 0 ------------------------------------------------------- Captured stdout setup ------------------------------------------------------- _django_db_setup called 2 times! E _________________________________________ ERROR at setup of test_kontrolluppgift_pdf_manual _________________________________________ [gw0] darwin -- Python 2.7.5 /Users/andreas/code/personalkollen/venv-2.7/bin/python ../pytest_django/pytest_django/plugin.py:177: in _django_db_marker > request.getfuncargvalue('db') ../pytest/_pytest/python.py:1319: in getfuncargvalue > return self._get_active_fixturedef(argname).cached_result[0] ../pytest/_pytest/python.py:1333: in _get_active_fixturedef > result = self._getfuncargvalue(fixturedef) ../pytest/_pytest/python.py:1385: in _getfuncargvalue > val = fixturedef.execute(request=subrequest) ../pytest/_pytest/python.py:1802: in execute > fixturedef = request._get_active_fixturedef(argname) ../pytest/_pytest/python.py:1333: in _get_active_fixturedef > result = self._getfuncargvalue(fixturedef) ../pytest/_pytest/python.py:1385: in _getfuncargvalue > val = fixturedef.execute(request=subrequest) ../pytest/_pytest/python.py:1835: in execute > self.yieldctx) ../pytest/_pytest/python.py:1761: in call_fixture_func > res = fixturefunc(**kwargs) ../pytest_django/pytest_django/fixtures.py:32: in _django_db_setup > assert 0 E assert 0 ------------------------------------------------------- Captured stdout setup ------------------------------------------------------- _django_db_setup called 3 times! ================================================= 1 passed, 2 error in 1.78 seconds ================================================= ``` The same test works fine without xdist: ``` $ py.test functional_tests/test_pdf_generation.py --capture=fd -v ======================================================== test session starts ======================================================== platform darwin -- Python 2.7.5 -- py-1.4.21.dev1 -- pytest-2.6.0.dev1 plugins: django, xdist, cache, instafail collected 3 items functional_tests/test_pdf_generation.py ... ===================================================== 3 passed in 2.09 seconds ====================================================== ``` This also happends with -n2, -n3 etc. It happens with pytest-xdist from revision 169 to latest tip, and pytest 2.5.2 to latest tip. From commits-noreply at bitbucket.org Sat Apr 12 23:01:26 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sat, 12 Apr 2014 21:01:26 -0000 Subject: [Pytest-commit] commit/pytest: flub: Update changelog with last change Message-ID: <20140412210126.2799.50019@app03.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/9eeb7bb8cf81/ Changeset: 9eeb7bb8cf81 User: flub Date: 2014-04-12 23:01:05 Summary: Update changelog with last change Affected #: 1 file diff -r 69395c44408cf30b21e7ed295a977130f46859b4 -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- change XPASS colour to yellow rather then red when tests are run + with -v. + - fix issue473: work around mock putting an unbound method into a class dict when double-patching. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 14 20:31:13 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 14 Apr 2014 18:31:13 -0000 Subject: [Pytest-commit] commit/pytest: flub: Close branch issue499 Message-ID: <20140414183113.3261.56030@app12.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/c3cd7cf4d60c/ Changeset: c3cd7cf4d60c Branch: issue499 User: flub Date: 2014-04-14 20:31:07 Summary: Close branch issue499 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 14 20:31:13 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 14 Apr 2014 18:31:13 -0000 Subject: [Pytest-commit] commit/pytest: flub: Merged in rouge8/pytest/issue499 (pull request #157) Message-ID: <20140414183113.14386.26881@app17.ash-private.bitbucket.org> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/a00590d97593/ Changeset: a00590d97593 User: flub Date: 2014-04-14 20:31:07 Summary: Merged in rouge8/pytest/issue499 (pull request #157) fix issue499: document selecting tests by node ID Affected #: 2 files diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r a00590d97593d00163772511aca3149f7ef0bae2 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -21,6 +21,9 @@ pass def test_another(): pass + class TestClass: + def test_method(self): + pass .. versionadded:: 2.2 @@ -29,25 +32,82 @@ $ py.test -v -m webtest =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - =================== 2 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + =================== 3 tests deselected by "-m 'webtest'" =================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== + +Selecing tests based on their node ID +------------------------------------- + +You can provide one or more :ref:`node IDs ` as positional +arguments to select only specified tests. This makes it easy to select +tests based on their module, class, method, or function name:: + + $ py.test -v test_server.py::TestClass::test_method + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +You can also select on the class:: + + $ py.test -v test_server.py::TestClass + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +Or select multiple nodes:: + + $ py.test -v test_server.py::TestClass test_server.py::test_send_http + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 8 items + + test_server.py:11: TestClass.test_method PASSED + test_server.py:3: test_send_http PASSED + + ========================= 2 passed in 0.01 seconds ========================= + + +.. _node-id: + +.. note:: + + Node IDs are of the form ``module.py::class::method`` or + ``module.py::function``. Node IDs control which tests are + collected, so ``module.py::class`` will select all test methods + on the class. Nodes are also created for each parameter of a + parametrized fixture or test, so selecting a parametrized test + must include the parameter value, e.g. + ``module.py::function[param]``. + + Node IDs for failing tests are displayed in the test summary info + when running py.test with the ``-rf`` option. You can also + construct Node IDs from the output of ``py.test --collectonly``. Using ``-k expr`` to select tests based on their name ------------------------------------------------------- @@ -62,38 +122,39 @@ $ py.test -v -k http # running with the above defined example module =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - ====================== 2 tests deselected by '-khttp' ====================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + ====================== 3 tests deselected by '-khttp' ====================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k "not send_http" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by '-knot send_http' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== Or to select "http" and "quick" tests:: $ py.test -k "http or quick" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED test_server.py:6: test_something_quick PASSED - ================= 1 tests deselected by '-khttp or quick' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================= 2 tests deselected by '-khttp or quick' ================== + ================== 2 passed, 2 deselected in 0.01 seconds ================== .. note:: diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r a00590d97593d00163772511aca3149f7ef0bae2 doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -49,6 +49,9 @@ # the "string expression", e.g. "MyClass and not method" # will select TestMyClass.test_something # but not TestMyClass.test_method_simple + py.test test_mod.py::test_func # only run tests that match the "node ID", + # e.g "test_mod.py::test_func" will select + # only test_func in test_mod.py Import 'pkg' and use its filesystem location to find and run tests:: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 14 20:31:13 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 14 Apr 2014 18:31:13 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140414183113.21127.74151@app07.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/69043f0b8025/ Changeset: 69043f0b8025 Branch: issue499 User: rouge8 Date: 2014-04-14 18:27:55 Summary: fix issue499: document selecting tests by node ID Affected #: 2 files diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r 69043f0b802502e544ad60b07c4a4c63aa988231 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -21,6 +21,9 @@ pass def test_another(): pass + class TestClass: + def test_method(self): + pass .. versionadded:: 2.2 @@ -29,25 +32,79 @@ $ py.test -v -m webtest =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - =================== 2 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + =================== 3 tests deselected by "-m 'webtest'" =================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== + +Selecing tests based on their node ID +------------------------------------- + +You can provide one or more node IDs as positional arguments to select +only specified tests. This makes it easy to select tests based on +their module, class, method, or function name:: + + $ py.test -v test_server.py::TestClass::test_method + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +You can also select on the class:: + + $ py.test -v test_server.py::TestClass + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +Or select multiple nodes:: + + $ py.test -v test_server.py::TestClass test_server.py::test_send_http + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 8 items + + test_server.py:11: TestClass.test_method PASSED + test_server.py:3: test_send_http PASSED + + ========================= 2 passed in 0.01 seconds ========================= + +.. note:: + + Node IDs are of the form ``module.py::class::method`` or + ``module.py::function``. Node IDs control which tests are + collected, so ``module.py::class`` will select all test methods + on the class. Nodes are also created for each parameter of a + parametrized fixture or test, so selecting a parametrized test + must include the parameter value, e.g. + ``module.py::function[param]``. + + Node IDs for failing tests are displayed in the test summary info + when running py.test with the ``-rf`` option. You can also + construct Node IDs from the output of ``py.test --collectonly``. Using ``-k expr`` to select tests based on their name ------------------------------------------------------- @@ -62,38 +119,39 @@ $ py.test -v -k http # running with the above defined example module =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - ====================== 2 tests deselected by '-khttp' ====================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + ====================== 3 tests deselected by '-khttp' ====================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k "not send_http" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by '-knot send_http' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== Or to select "http" and "quick" tests:: $ py.test -k "http or quick" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED test_server.py:6: test_something_quick PASSED - ================= 1 tests deselected by '-khttp or quick' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================= 2 tests deselected by '-khttp or quick' ================== + ================== 2 passed, 2 deselected in 0.01 seconds ================== .. note:: diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r 69043f0b802502e544ad60b07c4a4c63aa988231 doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -49,6 +49,9 @@ # the "string expression", e.g. "MyClass and not method" # will select TestMyClass.test_something # but not TestMyClass.test_method_simple + py.test test_mod.py::test_func # only run tests that match the "node ID", + # e.g "test_mod.py::test_func" will select + # only test_func in test_mod.py Import 'pkg' and use its filesystem location to find and run tests:: https://bitbucket.org/hpk42/pytest/commits/23b99e4408fd/ Changeset: 23b99e4408fd Branch: issue499 User: rouge8 Date: 2014-04-14 20:24:13 Summary: Interal link to node ID explanation Affected #: 1 file diff -r 69043f0b802502e544ad60b07c4a4c63aa988231 -r 23b99e4408fd903370e9ecdbd34a964aaeede684 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -56,9 +56,9 @@ Selecing tests based on their node ID ------------------------------------- -You can provide one or more node IDs as positional arguments to select -only specified tests. This makes it easy to select tests based on -their module, class, method, or function name:: +You can provide one or more :ref:`node IDs ` as positional +arguments to select only specified tests. This makes it easy to select +tests based on their module, class, method, or function name:: $ py.test -v test_server.py::TestClass::test_method =========================== test session starts ============================ @@ -92,6 +92,9 @@ ========================= 2 passed in 0.01 seconds ========================= + +.. _node-id: + .. note:: Node IDs are of the form ``module.py::class::method`` or https://bitbucket.org/hpk42/pytest/commits/a00590d97593/ Changeset: a00590d97593 User: flub Date: 2014-04-14 20:31:07 Summary: Merged in rouge8/pytest/issue499 (pull request #157) fix issue499: document selecting tests by node ID Affected #: 2 files diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r a00590d97593d00163772511aca3149f7ef0bae2 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -21,6 +21,9 @@ pass def test_another(): pass + class TestClass: + def test_method(self): + pass .. versionadded:: 2.2 @@ -29,25 +32,82 @@ $ py.test -v -m webtest =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - =================== 2 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + =================== 3 tests deselected by "-m 'webtest'" =================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== + +Selecing tests based on their node ID +------------------------------------- + +You can provide one or more :ref:`node IDs ` as positional +arguments to select only specified tests. This makes it easy to select +tests based on their module, class, method, or function name:: + + $ py.test -v test_server.py::TestClass::test_method + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +You can also select on the class:: + + $ py.test -v test_server.py::TestClass + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 4 items + + test_server.py:11: TestClass.test_method PASSED + + ========================= 1 passed in 0.01 seconds ========================= + +Or select multiple nodes:: + + $ py.test -v test_server.py::TestClass test_server.py::test_send_http + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 8 items + + test_server.py:11: TestClass.test_method PASSED + test_server.py:3: test_send_http PASSED + + ========================= 2 passed in 0.01 seconds ========================= + + +.. _node-id: + +.. note:: + + Node IDs are of the form ``module.py::class::method`` or + ``module.py::function``. Node IDs control which tests are + collected, so ``module.py::class`` will select all test methods + on the class. Nodes are also created for each parameter of a + parametrized fixture or test, so selecting a parametrized test + must include the parameter value, e.g. + ``module.py::function[param]``. + + Node IDs for failing tests are displayed in the test summary info + when running py.test with the ``-rf`` option. You can also + construct Node IDs from the output of ``py.test --collectonly``. Using ``-k expr`` to select tests based on their name ------------------------------------------------------- @@ -62,38 +122,39 @@ $ py.test -v -k http # running with the above defined example module =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED - ====================== 2 tests deselected by '-khttp' ====================== - ================== 1 passed, 2 deselected in 0.01 seconds ================== + ====================== 3 tests deselected by '-khttp' ====================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k "not send_http" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:6: test_something_quick PASSED test_server.py:8: test_another PASSED + test_server.py:11: TestClass.test_method PASSED ================= 1 tests deselected by '-knot send_http' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== Or to select "http" and "quick" tests:: $ py.test -k "http or quick" -v =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 3 items + collecting ... collected 4 items test_server.py:3: test_send_http PASSED test_server.py:6: test_something_quick PASSED - ================= 1 tests deselected by '-khttp or quick' ================== - ================== 2 passed, 1 deselected in 0.01 seconds ================== + ================= 2 tests deselected by '-khttp or quick' ================== + ================== 2 passed, 2 deselected in 0.01 seconds ================== .. note:: diff -r 9eeb7bb8cf81ebd997907ef43f57b4151fd6c002 -r a00590d97593d00163772511aca3149f7ef0bae2 doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -49,6 +49,9 @@ # the "string expression", e.g. "MyClass and not method" # will select TestMyClass.test_something # but not TestMyClass.test_method_simple + py.test test_mod.py::test_func # only run tests that match the "node ID", + # e.g "test_mod.py::test_func" will select + # only test_func in test_mod.py Import 'pkg' and use its filesystem location to find and run tests:: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Mon Apr 14 21:01:06 2014 From: issues-reply at bitbucket.org (Andy Freeland) Date: Mon, 14 Apr 2014 19:01:06 -0000 Subject: [Pytest-commit] Issue #504: include node IDs in py.test verbose output (hpk42/pytest) Message-ID: <20140414190106.3292.51835@app04.ash-private.bitbucket.org> New issue 504: include node IDs in py.test verbose output https://bitbucket.org/hpk42/pytest/issue/504/include-node-ids-in-pytest-verbose-output Andy Freeland: While I was working on #499 this morning, @hpk42 @flub and I talked about making the node ID more discoverable, since almost nobody knows that they exist. The way we discussed to do this is changing the output of `py.test -v` such that it includes the node ids. We still want to include the line numbers, so I wrote up an example output below. I'm not sure if that's the best way to include the line numbers, but they should probably be distinct from the node id. ``` $ py.test -v ================================================ test session starts ================================================= platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.6.0.dev1 -- /Users/rouge8/.virtualenvs/pytest/bin/python2.7 collected 4 items test_server.py::test_send_http (3): test_send_http PASSED test_server.py::test_something_quick (6): test_something_quick PASSED test_server.py::test_another (9): test_another PASSED test_server.py::TestClass::()::test_method (12): TestClass.test_method FAILED ``` From commits-noreply at bitbucket.org Tue Apr 15 02:11:43 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 15 Apr 2014 00:11:43 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20140415001143.22656.87067@app13.ash-private.bitbucket.org> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/cf0ce46a5189/ Changeset: cf0ce46a5189 User: flub Date: 2014-04-15 00:09:10 Summary: Improve error message if pytest.raises is used wrongly If the type is not checked then an incomprehensible error will occur later. This enforces the type and raies the same exception/msg as CPython does in that case. Docstring unmodified, just re-justified for pep8 compat. Affected #: 2 files diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r cf0ce46a5189c79bfab54eaab4b3fa6406e9c1ee _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -977,15 +977,16 @@ Performance note: ----------------- - Similar to caught exception objects in Python, explicitly clearing local - references to returned ``py.code.ExceptionInfo`` objects can help the Python - interpreter speed up its garbage collection. + Similar to caught exception objects in Python, explicitly clearing + local references to returned ``py.code.ExceptionInfo`` objects can + help the Python interpreter speed up its garbage collection. - Clearing those references breaks a reference cycle (``ExceptionInfo`` --> - caught exception --> frame stack raising the exception --> current frame - stack --> local variables --> ``ExceptionInfo``) which makes Python keep all - objects referenced from that cycle (including all local variables in the - current frame) alive until the next cyclic garbage collection run. See the + Clearing those references breaks a reference cycle + (``ExceptionInfo`` --> caught exception --> frame stack raising + the exception --> current frame stack --> local variables --> + ``ExceptionInfo``) which makes Python keep all objects referenced + from that cycle (including all local variables in the current + frame) alive until the next cyclic garbage collection run. See the official Python ``try`` statement documentation for more detailed information. @@ -995,7 +996,16 @@ # we want to catch a AssertionError # replace our subclass with the builtin one # see https://bitbucket.org/hpk42/pytest/issue/176/pytestraises - from _pytest.assertion.util import BuiltinAssertionError as ExpectedException + from _pytest.assertion.util import BuiltinAssertionError \ + as ExpectedException + msg = ("exceptions must be old-style classes or" + " derived from BaseException, not %s") + if isinstance(ExpectedException, tuple): + for exc in ExpectedException: + if not inspect.isclass(exc): + raise TypeError(msg % type(exc)) + elif not inspect.isclass(ExpectedException): + raise TypeError(msg % type(ExpectedException)) if not args: return RaisesContext(ExpectedException) diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r cf0ce46a5189c79bfab54eaab4b3fa6406e9c1ee testing/python/raises.py --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -62,3 +62,10 @@ '*3 passed*', ]) + def test_noclass(self): + with pytest.raises(TypeError): + pytest.raises('wrong', lambda: None) + + def test_tuple(self): + with pytest.raises((KeyError, ValueError)): + raise KeyError('oops') https://bitbucket.org/hpk42/pytest/commits/9572842277fa/ Changeset: 9572842277fa User: flub Date: 2014-04-15 00:12:29 Summary: Changelog for issue 475 Affected #: 1 file diff -r cf0ce46a5189c79bfab54eaab4b3fa6406e9c1ee -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ NEXT (2.6) ----------------------------------- +- fix issue 475: fail early and comprehensible if calling + pytest.raises with wrong exception type. + - change XPASS colour to yellow rather then red when tests are run with -v. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Tue Apr 15 19:40:23 2014 From: issues-reply at bitbucket.org (Rick Harding) Date: Tue, 15 Apr 2014 17:40:23 -0000 Subject: [Pytest-commit] Issue #505: Exception thrown in unloading after test run (hpk42/pytest) Message-ID: <20140415174023.803.15668@app09.ash-private.bitbucket.org> New issue 505: Exception thrown in unloading after test run https://bitbucket.org/hpk42/pytest/issue/505/exception-thrown-in-unloading-after-test Rick Harding: In trying to test my app with py.test I get a traceback. Full test run: http://23.253.20.78:8080/job/bookie-base/10/console ``` #!python Traceback (most recent call last): File "bin/py.test", line 9, in load_entry_point('pytest==2.5.2', 'console_scripts', 'py.test')() File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/config.py", line 20, in main return config.hook.pytest_cmdline_main(config=config) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 377, in __call__ return self._docall(methods, kwargs) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 388, in _docall res = mc.execute() File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 289, in execute res = method(**kwargs) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/main.py", line 112, in pytest_cmdline_main return wrap_session(config, _main) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/main.py", line 107, in wrap_session config.do_unconfigure() File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/config.py", line 613, in do_unconfigure self.hook.pytest_unconfigure(config=self) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 377, in __call__ return self._docall(methods, kwargs) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 388, in _docall res = mc.execute() File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/core.py", line 289, in execute res = method(**kwargs) File "/var/lib/jenkins/jobs/bookie-base/workspace/local/lib/python2.7/site-packages/_pytest/assertion/__init__.py", line 64, in pytest_unconfigure sys.meta_path.remove(hook) ValueError: list.remove(x): x not in list ``` In checking this out sys.meta_path[0] = nltk.compat.TkinterLoader object at 0x7f566da51410 hook = _pytest.assertion.rewrite.AssertionRewritingHook object at 0x7f5676834f50 So there's some conflict. I think a simple check if it's in the list before trying to run remove would work out. ``` #!python if hook is not None and hook in sys.meta_path ``` From commits-noreply at bitbucket.org Tue Apr 15 20:12:23 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 15 Apr 2014 18:12:23 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20140415181223.22023.58801@app05.ash-private.bitbucket.org> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/6fa216bce897/ Changeset: 6fa216bce897 Branch: issue504 User: rouge8 Date: 2014-04-14 23:42:02 Summary: issue504: verbose output displays node IDs for each test Replace the verbose per-test reporting format of `file:line test_name RESULT` with the node ID of the test, i.e. `file at line::class::method[param] RESULT`. This patch does not update the examples in the docs; @hpk42 has a script to regenerate those. Affected #: 8 files diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -41,3 +41,4 @@ Marc Schlaich Christopher Gilling Daniel Grana +Andy Freeland diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ NEXT (2.6) ----------------------------------- +- change -v output to include full node IDs of tests. Users can copy + a node ID from a test run, including line number, and use it as a + positional argument in order to run only a single test. + - change XPASS colour to yellow rather then red when tests are run with -v. diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -2,6 +2,7 @@ import py # DON't import pytest here because it causes import cycle troubles +import re import sys, os from _pytest import hookspec # the extension point definitions from _pytest.core import PluginManager @@ -180,7 +181,7 @@ a = option.attrs() arggroup.add_argument(*n, **a) # bash like autocompletion for dirs (appending '/') - optparser.add_argument(FILE_OR_DIR, nargs='*' + optparser.add_argument(FILE_OR_DIR, nargs='*', type=node_with_line_number, ).completer=filescompleter return optparser @@ -699,7 +700,7 @@ except ConftestImportFailure: e = sys.exc_info()[1] if ns.help or ns.version: - # we don't want to prevent --help/--version to work + # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.pluginmanager._warnings.append( "could not load initial conftests (%s)\n" % e.path) @@ -839,6 +840,12 @@ return {} +def node_with_line_number(string): + split = string.split('[') + split[0] = re.sub(r'@\d+', '', split[0]) + return '['.join(split) + + def setns(obj, dic): import pytest for name, value in dic.items(): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -382,9 +382,11 @@ line = str(fspath) if lineno is not None: lineno += 1 - line += ":" + str(lineno) + line += "@" + str(lineno) if domain: - line += ": " + str(domain) + split = str(domain).split('[') + split[0] = split[0].replace('.', '::') # don't replace '.' in params + line += "::" + '['.join(split) else: line = "[location]" return line + " " diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1620,22 +1620,22 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - test_mod1.py:1: test_func[s1] PASSED - test_mod2.py:1: test_func2[s1] PASSED - test_mod2.py:3: test_func3[s1-m1] PASSED - test_mod2.py:5: test_func3b[s1-m1] PASSED - test_mod2.py:3: test_func3[s1-m2] PASSED - test_mod2.py:5: test_func3b[s1-m2] PASSED - test_mod1.py:1: test_func[s2] PASSED - test_mod2.py:1: test_func2[s2] PASSED - test_mod2.py:3: test_func3[s2-m1] PASSED - test_mod2.py:5: test_func3b[s2-m1] PASSED - test_mod2.py:7: test_func4[m1] PASSED - test_mod2.py:3: test_func3[s2-m2] PASSED - test_mod2.py:5: test_func3b[s2-m2] PASSED - test_mod2.py:7: test_func4[m2] PASSED - test_mod1.py:3: test_func1[m1] PASSED - test_mod1.py:3: test_func1[m2] PASSED + test_mod1.py at 1::test_func[s1] PASSED + test_mod2.py at 1::test_func2[s1] PASSED + test_mod2.py at 3::test_func3[s1-m1] PASSED + test_mod2.py at 5::test_func3b[s1-m1] PASSED + test_mod2.py at 3::test_func3[s1-m2] PASSED + test_mod2.py at 5::test_func3b[s1-m2] PASSED + test_mod1.py at 1::test_func[s2] PASSED + test_mod2.py at 1::test_func2[s2] PASSED + test_mod2.py at 3::test_func3[s2-m1] PASSED + test_mod2.py at 5::test_func3b[s2-m1] PASSED + test_mod2.py at 7::test_func4[m1] PASSED + test_mod2.py at 3::test_func3[s2-m2] PASSED + test_mod2.py at 5::test_func3b[s2-m2] PASSED + test_mod2.py at 7::test_func4[m2] PASSED + test_mod1.py at 3::test_func1[m1] PASSED + test_mod1.py at 3::test_func1[m2] PASSED """) def test_class_ordering(self, testdir): @@ -1672,18 +1672,18 @@ """) result = testdir.runpytest("-vs") result.stdout.fnmatch_lines(""" - test_class_ordering.py:4: TestClass2.test_1[1-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[1-b] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-b] PASSED - test_class_ordering.py:9: TestClass.test_3[1-a] PASSED - test_class_ordering.py:9: TestClass.test_3[2-a] PASSED - test_class_ordering.py:9: TestClass.test_3[1-b] PASSED - test_class_ordering.py:9: TestClass.test_3[2-b] PASSED + test_class_ordering.py at 4::TestClass2::test_1[1-a] PASSED + test_class_ordering.py at 4::TestClass2::test_1[2-a] PASSED + test_class_ordering.py at 6::TestClass2::test_2[1-a] PASSED + test_class_ordering.py at 6::TestClass2::test_2[2-a] PASSED + test_class_ordering.py at 4::TestClass2::test_1[1-b] PASSED + test_class_ordering.py at 4::TestClass2::test_1[2-b] PASSED + test_class_ordering.py at 6::TestClass2::test_2[1-b] PASSED + test_class_ordering.py at 6::TestClass2::test_2[2-b] PASSED + test_class_ordering.py at 9::TestClass::test_3[1-a] PASSED + test_class_ordering.py at 9::TestClass::test_3[2-a] PASSED + test_class_ordering.py at 9::TestClass::test_3[1-b] PASSED + test_class_ordering.py at 9::TestClass::test_3[2-b] PASSED """) def test_parametrize_separated_order_higher_scope_first(self, testdir): @@ -2094,7 +2094,7 @@ def fix1(request): def f(): raise KeyError - request.addfinalizer(f) + request.addfinalizer(f) return object() l = [] @@ -2113,7 +2113,7 @@ *KeyError* *3 pass*2 error* """) - + def test_setupfunc_missing_funcarg(self, testdir): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_parseopt.py --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -145,6 +145,10 @@ assert args.R == True assert args.S == False + def test_parse_removes_line_number_from_positional_arguments(self, parser): + args = parser.parse(['path at 2::func', 'path2 at 5::func2[param with @]']) + assert getattr(args, parseopt.FILE_OR_DIR) == ['path::func', 'path2::func2[param with @]'] + def test_parse_defaultgetter(self): def defaultget(option): if not hasattr(option, 'type'): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -51,9 +51,9 @@ result = testdir.runpytest(*option.args) if option.verbose: result.stdout.fnmatch_lines([ - "*test_pass_skip_fail.py:2: *test_ok*PASS*", - "*test_pass_skip_fail.py:4: *test_skip*SKIP*", - "*test_pass_skip_fail.py:6: *test_func*FAIL*", + "*test_pass_skip_fail.py at 2::test_ok PASS*", + "*test_pass_skip_fail.py at 4::test_skip SKIP*", + "*test_pass_skip_fail.py at 6::test_func FAIL*", ]) else: result.stdout.fnmatch_lines([ @@ -126,7 +126,7 @@ ]) result = testdir.runpytest("-v", p2) result.stdout.fnmatch_lines([ - "*test_p2.py <- *test_p1.py:2: TestMore.test_p1*", + "*test_p2.py <- *test_p1.py at 2::TestMore::test_p1*", ]) def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): @@ -450,17 +450,17 @@ """) result = testdir.runpytest(p1, '-v') result.stdout.fnmatch_lines([ - "*test_verbose_reporting.py:2: test_fail*FAIL*", - "*test_verbose_reporting.py:4: test_pass*PASS*", - "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP*", - "*test_verbose_reporting.py:10: test_gen*FAIL*", + "*test_verbose_reporting.py at 2::test_fail *FAIL*", + "*test_verbose_reporting.py at 4::test_pass *PASS*", + "*test_verbose_reporting.py at 7::TestClass::test_skip *SKIP*", + "*test_verbose_reporting.py at 10::test_gen*0* *FAIL*", ]) assert result.ret == 1 pytestconfig.pluginmanager.skipifmissing("xdist") result = testdir.runpytest(p1, '-v', '-n 1') result.stdout.fnmatch_lines([ - "*FAIL*test_verbose_reporting.py:2: test_fail*", + "*FAIL*test_verbose_reporting.py at 2::test_fail*", ]) assert result.ret == 1 diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -29,8 +29,8 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - *MyTestCaseWithRunTest.runTest* - *MyTestCaseWithoutRunTest.test_something* + *MyTestCaseWithRunTest::runTest* + *MyTestCaseWithoutRunTest::test_something* *2 passed* """) https://bitbucket.org/hpk42/pytest/commits/229f3213f7a6/ Changeset: 229f3213f7a6 User: flub Date: 2014-04-15 17:43:38 Summary: Merge pull request #158, fixes issue 504 Affected #: 8 files diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -41,3 +41,4 @@ Marc Schlaich Christopher Gilling Daniel Grana +Andy Freeland diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ NEXT (2.6) ----------------------------------- +- change -v output to include full node IDs of tests. Users can copy + a node ID from a test run, including line number, and use it as a + positional argument in order to run only a single test. + - fix issue 475: fail early and comprehensible if calling pytest.raises with wrong exception type. diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -2,6 +2,7 @@ import py # DON't import pytest here because it causes import cycle troubles +import re import sys, os from _pytest import hookspec # the extension point definitions from _pytest.core import PluginManager @@ -180,7 +181,7 @@ a = option.attrs() arggroup.add_argument(*n, **a) # bash like autocompletion for dirs (appending '/') - optparser.add_argument(FILE_OR_DIR, nargs='*' + optparser.add_argument(FILE_OR_DIR, nargs='*', type=node_with_line_number, ).completer=filescompleter return optparser @@ -699,7 +700,7 @@ except ConftestImportFailure: e = sys.exc_info()[1] if ns.help or ns.version: - # we don't want to prevent --help/--version to work + # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.pluginmanager._warnings.append( "could not load initial conftests (%s)\n" % e.path) @@ -839,6 +840,12 @@ return {} +def node_with_line_number(string): + split = string.split('[') + split[0] = re.sub(r'@\d+', '', split[0]) + return '['.join(split) + + def setns(obj, dic): import pytest for name, value in dic.items(): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -382,9 +382,11 @@ line = str(fspath) if lineno is not None: lineno += 1 - line += ":" + str(lineno) + line += "@" + str(lineno) if domain: - line += ": " + str(domain) + split = str(domain).split('[') + split[0] = split[0].replace('.', '::') # don't replace '.' in params + line += "::" + '['.join(split) else: line = "[location]" return line + " " diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1620,22 +1620,22 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - test_mod1.py:1: test_func[s1] PASSED - test_mod2.py:1: test_func2[s1] PASSED - test_mod2.py:3: test_func3[s1-m1] PASSED - test_mod2.py:5: test_func3b[s1-m1] PASSED - test_mod2.py:3: test_func3[s1-m2] PASSED - test_mod2.py:5: test_func3b[s1-m2] PASSED - test_mod1.py:1: test_func[s2] PASSED - test_mod2.py:1: test_func2[s2] PASSED - test_mod2.py:3: test_func3[s2-m1] PASSED - test_mod2.py:5: test_func3b[s2-m1] PASSED - test_mod2.py:7: test_func4[m1] PASSED - test_mod2.py:3: test_func3[s2-m2] PASSED - test_mod2.py:5: test_func3b[s2-m2] PASSED - test_mod2.py:7: test_func4[m2] PASSED - test_mod1.py:3: test_func1[m1] PASSED - test_mod1.py:3: test_func1[m2] PASSED + test_mod1.py at 1::test_func[s1] PASSED + test_mod2.py at 1::test_func2[s1] PASSED + test_mod2.py at 3::test_func3[s1-m1] PASSED + test_mod2.py at 5::test_func3b[s1-m1] PASSED + test_mod2.py at 3::test_func3[s1-m2] PASSED + test_mod2.py at 5::test_func3b[s1-m2] PASSED + test_mod1.py at 1::test_func[s2] PASSED + test_mod2.py at 1::test_func2[s2] PASSED + test_mod2.py at 3::test_func3[s2-m1] PASSED + test_mod2.py at 5::test_func3b[s2-m1] PASSED + test_mod2.py at 7::test_func4[m1] PASSED + test_mod2.py at 3::test_func3[s2-m2] PASSED + test_mod2.py at 5::test_func3b[s2-m2] PASSED + test_mod2.py at 7::test_func4[m2] PASSED + test_mod1.py at 3::test_func1[m1] PASSED + test_mod1.py at 3::test_func1[m2] PASSED """) def test_class_ordering(self, testdir): @@ -1672,18 +1672,18 @@ """) result = testdir.runpytest("-vs") result.stdout.fnmatch_lines(""" - test_class_ordering.py:4: TestClass2.test_1[1-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[1-b] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-b] PASSED - test_class_ordering.py:9: TestClass.test_3[1-a] PASSED - test_class_ordering.py:9: TestClass.test_3[2-a] PASSED - test_class_ordering.py:9: TestClass.test_3[1-b] PASSED - test_class_ordering.py:9: TestClass.test_3[2-b] PASSED + test_class_ordering.py at 4::TestClass2::test_1[1-a] PASSED + test_class_ordering.py at 4::TestClass2::test_1[2-a] PASSED + test_class_ordering.py at 6::TestClass2::test_2[1-a] PASSED + test_class_ordering.py at 6::TestClass2::test_2[2-a] PASSED + test_class_ordering.py at 4::TestClass2::test_1[1-b] PASSED + test_class_ordering.py at 4::TestClass2::test_1[2-b] PASSED + test_class_ordering.py at 6::TestClass2::test_2[1-b] PASSED + test_class_ordering.py at 6::TestClass2::test_2[2-b] PASSED + test_class_ordering.py at 9::TestClass::test_3[1-a] PASSED + test_class_ordering.py at 9::TestClass::test_3[2-a] PASSED + test_class_ordering.py at 9::TestClass::test_3[1-b] PASSED + test_class_ordering.py at 9::TestClass::test_3[2-b] PASSED """) def test_parametrize_separated_order_higher_scope_first(self, testdir): @@ -2094,7 +2094,7 @@ def fix1(request): def f(): raise KeyError - request.addfinalizer(f) + request.addfinalizer(f) return object() l = [] @@ -2113,7 +2113,7 @@ *KeyError* *3 pass*2 error* """) - + def test_setupfunc_missing_funcarg(self, testdir): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_parseopt.py --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -145,6 +145,10 @@ assert args.R == True assert args.S == False + def test_parse_removes_line_number_from_positional_arguments(self, parser): + args = parser.parse(['path at 2::func', 'path2 at 5::func2[param with @]']) + assert getattr(args, parseopt.FILE_OR_DIR) == ['path::func', 'path2::func2[param with @]'] + def test_parse_defaultgetter(self): def defaultget(option): if not hasattr(option, 'type'): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -51,9 +51,9 @@ result = testdir.runpytest(*option.args) if option.verbose: result.stdout.fnmatch_lines([ - "*test_pass_skip_fail.py:2: *test_ok*PASS*", - "*test_pass_skip_fail.py:4: *test_skip*SKIP*", - "*test_pass_skip_fail.py:6: *test_func*FAIL*", + "*test_pass_skip_fail.py at 2::test_ok PASS*", + "*test_pass_skip_fail.py at 4::test_skip SKIP*", + "*test_pass_skip_fail.py at 6::test_func FAIL*", ]) else: result.stdout.fnmatch_lines([ @@ -126,7 +126,7 @@ ]) result = testdir.runpytest("-v", p2) result.stdout.fnmatch_lines([ - "*test_p2.py <- *test_p1.py:2: TestMore.test_p1*", + "*test_p2.py <- *test_p1.py at 2::TestMore::test_p1*", ]) def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): @@ -450,17 +450,17 @@ """) result = testdir.runpytest(p1, '-v') result.stdout.fnmatch_lines([ - "*test_verbose_reporting.py:2: test_fail*FAIL*", - "*test_verbose_reporting.py:4: test_pass*PASS*", - "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP*", - "*test_verbose_reporting.py:10: test_gen*FAIL*", + "*test_verbose_reporting.py at 2::test_fail *FAIL*", + "*test_verbose_reporting.py at 4::test_pass *PASS*", + "*test_verbose_reporting.py at 7::TestClass::test_skip *SKIP*", + "*test_verbose_reporting.py at 10::test_gen*0* *FAIL*", ]) assert result.ret == 1 pytestconfig.pluginmanager.skipifmissing("xdist") result = testdir.runpytest(p1, '-v', '-n 1') result.stdout.fnmatch_lines([ - "*FAIL*test_verbose_reporting.py:2: test_fail*", + "*FAIL*test_verbose_reporting.py at 2::test_fail*", ]) assert result.ret == 1 diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -29,8 +29,8 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - *MyTestCaseWithRunTest.runTest* - *MyTestCaseWithoutRunTest.test_something* + *MyTestCaseWithRunTest::runTest* + *MyTestCaseWithoutRunTest::test_something* *2 passed* """) https://bitbucket.org/hpk42/pytest/commits/ffe92a9b0441/ Changeset: ffe92a9b0441 Branch: issue504 User: flub Date: 2014-04-15 17:46:34 Summary: Closing bugfix branch Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From issues-reply at bitbucket.org Thu Apr 17 10:09:38 2014 From: issues-reply at bitbucket.org (Matthew Iversen) Date: Thu, 17 Apr 2014 08:09:38 -0000 Subject: [Pytest-commit] Issue #166: Unable to find python on path (~/bin) (hpk42/tox) Message-ID: <20140417080938.10223.25447@app08.ash-private.bitbucket.org> New issue 166: Unable to find python on path (~/bin) https://bitbucket.org/hpk42/tox/issue/166/unable-to-find-python-on-path-bin Matthew Iversen: I have my own path with symlinked custom built pythons, linked in `~/bin`. ``` $ cat ~/.bashrc ... # Add ~/bin to path export PATH="~/bin:$PATH" ... ``` ``` $ ll ~/bin ... lrwxrwxrwx 1 ivo ivo 34 Feb 28 01:09 python2.6 -> /home/ivo/local/py26/bin/python2.6 lrwxrwxrwx 1 ivo ivo 34 Feb 28 01:09 python3.2 -> /home/ivo/local/py32/bin/python3.2 ... ``` ``` $ which python2.6 /home/ivo/bin/python2.6 ``` Tox can't find these pythons. It's because of a bug in `py` - https://bitbucket.org/hpk42/py/issue/44/cant-handle-standard-unix-when-using ``` $ tox -e py26 GLOB sdist-make: /home/ivo/code/pypa/pip/setup.py py26 create: /home/ivo/code/pypa/pip/.tox/py26 ERROR: InterpreterNotFound: python2.6 _____________________________________________________________________________________________________ summary ______________________________________________________________________________________________________ ERROR: py26: InterpreterNotFound: python2.6 ``` Ronny on IRC asked me to file an associated issue for tox as well. From issues-reply at bitbucket.org Sat Apr 19 13:53:56 2014 From: issues-reply at bitbucket.org (Christoph Deil) Date: Sat, 19 Apr 2014 11:53:56 -0000 Subject: [Pytest-commit] Issue #506: Add option to run "flaky" tests repeatedly to catch rare errors (hpk42/pytest) Message-ID: <20140419115356.17601.40808@app10.ash-private.bitbucket.org> New issue 506: Add option to run "flaky" tests repeatedly to catch rare errors https://bitbucket.org/hpk42/pytest/issue/506/add-option-to-run-flaky-tests-repeatedly Christoph Deil: Would it be possible to add an option to pytest to run tests repeatedly (either N times or until forever)? This would be useful to catch non-deterministic "flaky" test failures such as e.g. ``` #!python import random def flaky_test(): number = random.randint(0, 100) assert number != 42 ``` We currently have this problem in Astropy where some (probably socket-related) test failures "randomly" show up on travis-ci and it would be really helpful to be able to reproduce the failure locally: https://github.com/astropy/astropy/issues/2064 From issues-reply at bitbucket.org Tue Apr 22 17:35:45 2014 From: issues-reply at bitbucket.org (Matthew Story) Date: Tue, 22 Apr 2014 15:35:45 -0000 Subject: [Pytest-commit] Issue #507: pytest-2.5.2: Assertion breaks with dictionary comprehention (hpk42/pytest) Message-ID: <20140422153545.30359.4969@app03.ash-private.bitbucket.org> New issue 507: pytest-2.5.2: Assertion breaks with dictionary comprehention https://bitbucket.org/hpk42/pytest/issue/507/pytest-252-assertion-breaks-with Matthew Story: In version 2.5.2 assertions break when you use a comprehension on one side of the comparison: ``` def assert_subsetof(d1, d2): '''Assert d1 is a subset of d2, with equal values for all defined keys''' > assert {k:d2[k] for k in d1 if d2.has_key(k)} == d1 E AssertionError: NameError: << global name 'd2' is not defined /usr/axm/lib/python2.7/site-packages/apitests/assertions.py:4: AssertionError ``` When writing in pure python, the same operation works fine: ``` #!python >>> def foo(d1, d2): ... assert {k:d2[k] for k in d1 if d2.has_key(k)} == d1 ... >>> foo({'a': 'b'}, {'a': 'b', 'b': 'c'}) ``` From issues-reply at bitbucket.org Wed Apr 23 18:50:22 2014 From: issues-reply at bitbucket.org (Mikhail Korobov) Date: Wed, 23 Apr 2014 16:50:22 -0000 Subject: [Pytest-commit] Issue #167: Add an option to run commands after virtualenv creation but before other steps (hpk42/tox) Message-ID: <20140423165022.28480.27204@app06.ash-private.bitbucket.org> New issue 167: Add an option to run commands after virtualenv creation but before other steps https://bitbucket.org/hpk42/tox/issue/167/add-an-option-to-run-commands-after Mikhail Korobov: Hi, I first mentioned this idea at https://bitbucket.org/hpk42/tox/issue/42/dependencies-should-be-installed-one-at-a (it was shut down then). I think it deserves its own ticket :) The problem is the following: 1. Some packages require certain software to build. Most common case - a C/Cython extension that uses numpy or scipy C-API. 2. One solution is to put this software to `deps` section. The issue is that it can be hard/slow to install numpy and scipy to virtualenv using pip. System packages is not a solution either because you may need different versions. Also, there could be chains like "my package needs A to build, and A needs B to build", so you can't put both A and B into deps. As an alternative, I can use Anaconda to get numpy, scipy and similar packages to virtualenv. It is fast and works quite well. But you need to 1. run 'pip install conda' and 'conda init' after virtualenv creation; 2. then run 'conda install numpy scipy --yes'; 3. and only then install other packages and the package being tested. Both these steps should happen *before* installation of the package being tested. I can't set `install_commans` to `conda install {packages}` because conda can't install local zip packages. There are other possible uses of this: for example, packages may need C/C++ libraries to build, and one may fetch them and install to virtualenv. So, I'd like to be able to run some commands after virtualenv creation but before installation of my package (and better before installation of the dependencies). From issues-reply at bitbucket.org Fri Apr 25 12:39:54 2014 From: issues-reply at bitbucket.org (dwt) Date: Fri, 25 Apr 2014 10:39:54 -0000 Subject: [Pytest-commit] Issue #168: Tox fails installing sdist in python3.3 (hpk42/tox) Message-ID: <20140425103954.23392.574@app02.ash-private.bitbucket.org> New issue 168: Tox fails installing sdist in python3.3 https://bitbucket.org/hpk42/tox/issue/168/tox-fails-installing-sdist-in-python33 dwt: I'm hosting tox on python 2.7and am getting errors like this: ``` #!shell tox GLOB sdist-make: /Users/dwt/Code/Projekte/pyexpect/setup.py py26 inst-nodeps: /Users/dwt/Code/Projekte/pyexpect/.tox/dist/pyexpect-1.0.5.zip py26 runtests: PYTHONHASHSEED='3320894281' py26 runtests: commands[0] | /Users/dwt/Code/Projekte/pyexpect/.tox/py26/bin/python pyexpect.py .................................. ---------------------------------------------------------------------- Ran 34 tests in 0.245s OK py27 inst-nodeps: /Users/dwt/Code/Projekte/pyexpect/.tox/dist/pyexpect-1.0.5.zip py27 runtests: PYTHONHASHSEED='3320894281' py27 runtests: commands[0] | /Users/dwt/Code/Projekte/pyexpect/.tox/py27/bin/python pyexpect.py .................................. ---------------------------------------------------------------------- Ran 34 tests in 0.182s OK Traceback (most recent call last): File "/Users/dwt/Library/Python/2.7/bin/tox", line 9, in load_entry_point('tox==1.7.1', 'console_scripts', 'tox')() File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/_cmdline.py", line 26, in main retcode = Session(config).runcommand() File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/_cmdline.py", line 303, in runcommand return self.subcommand_test() File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/_cmdline.py", line 440, in subcommand_test if self.setupenv(venv): File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/_cmdline.py", line 370, in setupenv envlog.set_python_info(commandpath) File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/result.py", line 47, in set_python_info "import sys; " File "/Users/dwt/Library/Python/2.7/lib/python/site-packages/py-1.4.20-py2.7.egg/py/_path/local.py", line 673, in sysexec stdout, stderr,) py.process.cmdexec.Error: ExecutionFailed: 1 /Users/dwt/Code/Projekte/pyexpect/.tox/py33/bin/python Your PYTHONPATH points to a site-packages dir for Python 2.x but you are running Python 3.x! PYTHONPATH is currently: "/sw/lib/python2.6/site-packages:/Users/dwt/Library/Homebrew/lib/python2.7/site-packages" You should `unset PYTHONPATH` to fix this. ``` This is what my .tox-file looks ``` [tox] envlist = py26, py27, py33 #, pypy [testenv] commands = {envpython} pyexpect.py [testenv:py33] setenv = PYTHONPATH = ``` As far as I can tell, what is missing is that at https://bitbucket.org/hpk42/tox/src/0c390972b576b50dc0e78bd5cbb8e6a164f15f8b/tox/result.py?at=default#cl-46 the call to pythonexecutable.sysexec() is missing the environment variable ```PYTHONPATH=''``` that is set in the setenv line in the tox file. I don't however understand how to do that. :/ So no patch from me for now. Just quickly splicing in a ```env=dict(PYTHONPATH='')``` into that call seems to work around the issue. Do you need any more information to fix this? From issues-reply at bitbucket.org Fri Apr 25 12:47:57 2014 From: issues-reply at bitbucket.org (dwt) Date: Fri, 25 Apr 2014 10:47:57 -0000 Subject: [Pytest-commit] Issue #169: Tox fails to create python 3.3 venv (hpk42/tox) Message-ID: <20140425104757.20974.69500@app12.ash-private.bitbucket.org> New issue 169: Tox fails to create python 3.3 venv https://bitbucket.org/hpk42/tox/issue/169/tox-fails-to-create-python-33-venv dwt: While trying to create a virtualenv for python 3.3 I'm getting the following error: ``` % tox --develop -v --recreate -e py33 using tox.ini: /Users/dwt/Code/Projekte/pyexpect/tox.ini using tox-1.7.1 from /Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/__init__.pyc py33 recreate: /Users/dwt/Code/Projekte/pyexpect/.tox/py33 ERROR: UnsupportedInterpreter: python2.5 is not supported anymore, sorry ___________ summary ____________ ERROR: py33: UnsupportedInterpreter: python2.5 is not supported anymore, sorry ``` What is supposed to happen is this: ``` % PYTHONPATH='' tox --develop -v --recreate -e py33 using tox.ini: /Users/dwt/Code/Projekte/pyexpect/tox.ini using tox-1.7.1 from /Users/dwt/Library/Python/2.7/lib/python/site-packages/tox-1.7.1-py2.7.egg/tox/__init__.pyc py33 recreate: /Users/dwt/Code/Projekte/pyexpect/.tox/py33 /Users/dwt/Code/Projekte/pyexpect/.tox$ /usr/bin/python -mvirtualenv --setuptools --python /Users/dwt/Library/Homebrew/bin/python3.3 py33 >/Users/dwt/Code/Projekte/pyexpect/.tox/py33/log/py33-0.log py33 develop-inst: /Users/dwt/Code/Projekte/pyexpect /Users/dwt/Code/Projekte/pyexpect$ /Users/dwt/Code/Projekte/pyexpect/.tox/py33/bin/pip install --pre -e /Users/dwt/Code/Projekte/pyexpect >/Users/dwt/Code/Projekte/pyexpect/.tox/py33/log/py33-1.log py33 runtests: PYTHONHASHSEED='1252960215' py33 runtests: commands[0] | /Users/dwt/Code/Projekte/pyexpect/.tox/py33/bin/python pyexpect.py /Users/dwt/Code/Projekte/pyexpect$ /Users/dwt/Code/Projekte/pyexpect/.tox/py33/bin/python pyexpect.py .................................. ---------------------------------------------------------------------- Ran 34 tests in 0.272s OK ___________ summary ____________ py33: commands succeeded congratulations :) ``` My tox file specifies this variable: ``` [tox] envlist = py26, py27, py33 #, pypy [testenv] commands = {envpython} pyexpect.py [testenv:py33] setenv = PYTHONPATH = ``` So it seems tox is missing to actually hand in that virtualenv variable to the python 3.3 in question. From commits-noreply at bitbucket.org Sun Apr 27 08:06:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:06:47 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Close branch doc_update_jenkins_example_3 Message-ID: <20140427060647.7384.46853@app05.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/d631ce0640cc/ Changeset: d631ce0640cc Branch: doc_update_jenkins_example_3 User: hpk42 Date: 2014-04-27 08:06:41 Summary: Close branch doc_update_jenkins_example_3 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:06:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:06:47 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427060647.28246.99249@app06.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/eae0987f3d58/ Changeset: eae0987f3d58 Branch: doc_update_jenkins_example_3 User: Marc Abramowitz Date: 2014-04-24 16:58:29 Summary: doc: Update Jenkins example to use code-block Affected #: 1 file diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r eae0987f3d58e2ae276041e547075676a9d24caf doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -32,7 +32,9 @@ The last point requires that your test command creates JunitXML files, for example with ``py.test`` it is done like this: - commands=py.test --junitxml=junit-{envname}.xml +.. code-block:: ini + + commands = py.test --junitxml=junit-{envname}.xml https://bitbucket.org/hpk42/tox/commits/2abae477c284/ Changeset: 2abae477c284 User: hpk42 Date: 2014-04-27 08:06:41 Summary: Merged in msabramo/tox/doc_update_jenkins_example_3 (pull request #94) doc: Update Jenkins example to use code-block Affected #: 1 file diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 2abae477c284db3715ea9ff16dd62d03f7714677 doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -32,7 +32,9 @@ The last point requires that your test command creates JunitXML files, for example with ``py.test`` it is done like this: - commands=py.test --junitxml=junit-{envname}.xml +.. code-block:: ini + + commands = py.test --junitxml=junit-{envname}.xml Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:06:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:06:47 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Merged in msabramo/tox/doc_update_jenkins_example_3 (pull request #94) Message-ID: <20140427060647.17500.43054@app13.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/2abae477c284/ Changeset: 2abae477c284 User: hpk42 Date: 2014-04-27 08:06:41 Summary: Merged in msabramo/tox/doc_update_jenkins_example_3 (pull request #94) doc: Update Jenkins example to use code-block Affected #: 1 file diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 2abae477c284db3715ea9ff16dd62d03f7714677 doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -32,7 +32,9 @@ The last point requires that your test command creates JunitXML files, for example with ``py.test`` it is done like this: - commands=py.test --junitxml=junit-{envname}.xml +.. code-block:: ini + + commands = py.test --junitxml=junit-{envname}.xml Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:06 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:06 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Merged in msabramo/tox/tox_quickstart_add_py34 (pull request #98) Message-ID: <20140427060706.5752.11086@app05.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/66591c4d29b0/ Changeset: 66591c4d29b0 User: hpk42 Date: 2014-04-27 08:07:01 Summary: Merged in msabramo/tox/tox_quickstart_add_py34 (pull request #98) tox-quickstart: Add py34 Affected #: 1 file diff -r 2abae477c284db3715ea9ff16dd62d03f7714677 -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 tox/_quickstart.py --- a/tox/_quickstart.py +++ b/tox/_quickstart.py @@ -56,7 +56,7 @@ term_input = input -all_envs = ['py26', 'py27', 'py32', 'py33', 'pypy', 'jython'] +all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'pypy', 'jython'] PROMPT_PREFIX = '> ' Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:06 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:06 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427060706.11223.6146@app12.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/94842aabbbcc/ Changeset: 94842aabbbcc Branch: tox_quickstart_add_py34 User: Marc Abramowitz Date: 2014-04-25 22:32:57 Summary: tox-quickstart: Add py34 Affected #: 1 file diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 94842aabbbcc7acb57cc410c1e122d7856664d56 tox/_quickstart.py --- a/tox/_quickstart.py +++ b/tox/_quickstart.py @@ -56,7 +56,7 @@ term_input = input -all_envs = ['py26', 'py27', 'py32', 'py33', 'pypy', 'jython'] +all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'pypy', 'jython'] PROMPT_PREFIX = '> ' https://bitbucket.org/hpk42/tox/commits/66591c4d29b0/ Changeset: 66591c4d29b0 User: hpk42 Date: 2014-04-27 08:07:01 Summary: Merged in msabramo/tox/tox_quickstart_add_py34 (pull request #98) tox-quickstart: Add py34 Affected #: 1 file diff -r 2abae477c284db3715ea9ff16dd62d03f7714677 -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 tox/_quickstart.py --- a/tox/_quickstart.py +++ b/tox/_quickstart.py @@ -56,7 +56,7 @@ term_input = input -all_envs = ['py26', 'py27', 'py32', 'py33', 'pypy', 'jython'] +all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'pypy', 'jython'] PROMPT_PREFIX = '> ' Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:07 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:07 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Close branch tox_quickstart_add_py34 Message-ID: <20140427060707.26416.68567@app17.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/a1159ac2e88e/ Changeset: a1159ac2e88e Branch: tox_quickstart_add_py34 User: hpk42 Date: 2014-04-27 08:07:01 Summary: Close branch tox_quickstart_add_py34 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:47 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Merged in msabramo/tox/doc_fix_broken_links (pull request #96) Message-ID: <20140427060747.28874.44270@app02.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/e2e54c777565/ Changeset: e2e54c777565 User: hpk42 Date: 2014-04-27 08:07:43 Summary: Merged in msabramo/tox/doc_fix_broken_links (pull request #96) doc: Fix broken links Affected #: 2 files diff -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 -r e2e54c77756557d95614c2e2e207f537af3be929 doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -59,7 +59,7 @@ exec urllib.urlopen(url).read() in d d['cmdline'](['--recreate']) -The downloaded `toxbootstrap.py`_ file downloads all neccessary files to +The downloaded `toxbootstrap.py` file downloads all neccessary files to install ``tox`` in a virtual sub environment. Notes: * uncomment the line containing ``USETOXDEV`` to use the latest @@ -70,7 +70,6 @@ will cause tox to reinstall all virtual environments all the time which is often what one wants in CI server contexts) -.. _`toxbootstrap.py`: https://bitbucket.org/hpk42/tox/raw/default/toxbootstrap.py Integrating "sphinx" documentation checks in a Jenkins job ---------------------------------------------------------------- diff -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 -r e2e54c77756557d95614c2e2e207f537af3be929 doc/example/pytest.txt --- a/doc/example/pytest.txt +++ b/doc/example/pytest.txt @@ -105,7 +105,7 @@ parent directory to ``sys.path`` but they won't be copied to other places or be found by Python's import system outside of pytest. -.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#package-name +.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#test-package-name .. include:: ../links.txt Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:47 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Close branch doc_fix_broken_links Message-ID: <20140427060747.7384.33139@app05.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/7767f6fc144d/ Changeset: 7767f6fc144d Branch: doc_fix_broken_links User: hpk42 Date: 2014-04-27 08:07:43 Summary: Close branch doc_fix_broken_links Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:07:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:07:47 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427060747.31529.47159@app10.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/6e6e1cbb74d7/ Changeset: 6e6e1cbb74d7 Branch: doc_fix_broken_links User: Marc Abramowitz Date: 2014-04-24 19:27:16 Summary: doc: Fix broken links Affected #: 2 files diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 6e6e1cbb74d7f0539ef06997954318f26c56a912 doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -57,7 +57,7 @@ exec urllib.urlopen(url).read() in d d['cmdline'](['--recreate']) -The downloaded `toxbootstrap.py`_ file downloads all neccessary files to +The downloaded `toxbootstrap.py` file downloads all neccessary files to install ``tox`` in a virtual sub environment. Notes: * uncomment the line containing ``USETOXDEV`` to use the latest @@ -68,7 +68,6 @@ will cause tox to reinstall all virtual environments all the time which is often what one wants in CI server contexts) -.. _`toxbootstrap.py`: https://bitbucket.org/hpk42/tox/raw/default/toxbootstrap.py Integrating "sphinx" documentation checks in a Jenkins job ---------------------------------------------------------------- diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 6e6e1cbb74d7f0539ef06997954318f26c56a912 doc/example/pytest.txt --- a/doc/example/pytest.txt +++ b/doc/example/pytest.txt @@ -105,7 +105,7 @@ parent directory to ``sys.path`` but they won't be copied to other places or be found by Python's import system outside of pytest. -.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#package-name +.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#test-package-name .. include:: ../links.txt https://bitbucket.org/hpk42/tox/commits/e2e54c777565/ Changeset: e2e54c777565 User: hpk42 Date: 2014-04-27 08:07:43 Summary: Merged in msabramo/tox/doc_fix_broken_links (pull request #96) doc: Fix broken links Affected #: 2 files diff -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 -r e2e54c77756557d95614c2e2e207f537af3be929 doc/example/jenkins.txt --- a/doc/example/jenkins.txt +++ b/doc/example/jenkins.txt @@ -59,7 +59,7 @@ exec urllib.urlopen(url).read() in d d['cmdline'](['--recreate']) -The downloaded `toxbootstrap.py`_ file downloads all neccessary files to +The downloaded `toxbootstrap.py` file downloads all neccessary files to install ``tox`` in a virtual sub environment. Notes: * uncomment the line containing ``USETOXDEV`` to use the latest @@ -70,7 +70,6 @@ will cause tox to reinstall all virtual environments all the time which is often what one wants in CI server contexts) -.. _`toxbootstrap.py`: https://bitbucket.org/hpk42/tox/raw/default/toxbootstrap.py Integrating "sphinx" documentation checks in a Jenkins job ---------------------------------------------------------------- diff -r 66591c4d29b0f70843e6f6e0d8494a7f833f4cb3 -r e2e54c77756557d95614c2e2e207f537af3be929 doc/example/pytest.txt --- a/doc/example/pytest.txt +++ b/doc/example/pytest.txt @@ -105,7 +105,7 @@ parent directory to ``sys.path`` but they won't be copied to other places or be found by Python's import system outside of pytest. -.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#package-name +.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#test-package-name .. include:: ../links.txt Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:08:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:08:10 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427060810.31529.59707@app10.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/43fc06e4cef6/ Changeset: 43fc06e4cef6 Branch: tox_add_py33_and_py34 User: Marc Abramowitz Date: 2014-04-24 17:04:56 Summary: tox.ini: Add py33 and py34 Affected #: 1 file diff -r 0c390972b576b50dc0e78bd5cbb8e6a164f15f8b -r 43fc06e4cef6079d251bbc076b529901012506e9 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,pypy +envlist=py27,py33,py26,py32,py33,py34,pypy [testenv:X] commands=echo {posargs} https://bitbucket.org/hpk42/tox/commits/6cda25d37c50/ Changeset: 6cda25d37c50 User: hpk42 Date: 2014-04-27 08:08:06 Summary: Merged in msabramo/tox/tox_add_py33_and_py34 (pull request #95) tox.ini: Add py33 and py34 Affected #: 1 file diff -r e2e54c77756557d95614c2e2e207f537af3be929 -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,pypy +envlist=py27,py33,py26,py32,py33,py34,pypy [testenv:X] commands=echo {posargs} Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:08:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:08:10 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Merged in msabramo/tox/tox_add_py33_and_py34 (pull request #95) Message-ID: <20140427060810.26416.91566@app17.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/6cda25d37c50/ Changeset: 6cda25d37c50 User: hpk42 Date: 2014-04-27 08:08:06 Summary: Merged in msabramo/tox/tox_add_py33_and_py34 (pull request #95) tox.ini: Add py33 and py34 Affected #: 1 file diff -r e2e54c77756557d95614c2e2e207f537af3be929 -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,pypy +envlist=py27,py33,py26,py32,py33,py34,pypy [testenv:X] commands=echo {posargs} Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:08:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:08:10 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Close branch tox_add_py33_and_py34 Message-ID: <20140427060810.7070.70612@app03.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/4f0917fafbbd/ Changeset: 4f0917fafbbd Branch: tox_add_py33_and_py34 User: hpk42 Date: 2014-04-27 08:08:06 Summary: Close branch tox_add_py33_and_py34 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:18:24 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:18:24 -0000 Subject: [Pytest-commit] commit/pytest-pep8: hpk42: fix tests to accomodate newer pep version which is more sensitive Message-ID: <20140427061824.6434.97489@app11.ash-private.bitbucket.org> 1 new commit in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/commits/e2656c21b42a/ Changeset: e2656c21b42a User: hpk42 Date: 2014-04-27 08:18:16 Summary: fix tests to accomodate newer pep version which is more sensitive to detecting "E265 too many spaces" add py34 to tox.ini Affected #: 3 files diff -r e9317b1b8eb91e0bd3e46b650d765fff616279d1 -r e2656c21b42a700f57723b646b621fc7e06a6e5a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +xxx +---------- + +- fix tests to accomodate newer pep version which is more sensitive + to detecting "E265 too many spaces" + +- add py34 to tox.ini + 1.0.6 -------------- diff -r e9317b1b8eb91e0bd3e46b650d765fff616279d1 -r e2656c21b42a700f57723b646b621fc7e06a6e5a test_pep8.py --- a/test_pep8.py +++ b/test_pep8.py @@ -13,7 +13,7 @@ def pytest_funcarg__example(self, request): testdir = request.getfuncargvalue("testdir") p = testdir.makepyfile("") - p.write("class AClass:\n pass\n \n\n#too many spaces") + p.write("class AClass:\n pass\n \n\n# too many spaces") return p def test_ignores(self, tmpdir): @@ -45,7 +45,7 @@ def test_w293w292(self, testdir, example): result = testdir.runpytest("--pep8", ) result.stdout.fnmatch_lines([ - #"*plugins*pep8*", + # "*plugins*pep8*", "*W293*", "*W292*", ]) @@ -55,7 +55,7 @@ testdir.tmpdir.ensure("hello.py") result = testdir.runpytest("--pep8", ) result.stdout.fnmatch_lines([ - #"*plugins*pep8*", + # "*plugins*pep8*", "*W293*", "*W292*", "*1 failed*1 passed*", diff -r e9317b1b8eb91e0bd3e46b650d765fff616279d1 -r e2656c21b42a700f57723b646b621fc7e06a6e5a tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py25,py27,py27-pytesttrunk,py33,py-xdist +envlist=py26,py27,py27-pytesttrunk,py33,py-xdist,py34 [testenv] deps=pytest Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 08:21:50 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 06:21:50 -0000 Subject: [Pytest-commit] commit/pytest-pep8: 2 new changesets Message-ID: <20140427062150.10177.49793@app09.ash-private.bitbucket.org> 2 new commits in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/commits/394c799c92a2/ Changeset: 394c799c92a2 User: hpk42 Date: 2014-04-27 08:21:40 Summary: bump version to 1.0.6 and release to pypi Affected #: 3 files diff -r e2656c21b42a700f57723b646b621fc7e06a6e5a -r 394c799c92a2450ebc94ea04a006f0875fc0f89a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -xxx +1.0.6 ---------- - fix tests to accomodate newer pep version which is more sensitive @@ -6,9 +6,6 @@ - add py34 to tox.ini -1.0.6 --------------- - - clarified in setup.py that license is MIT 1.0.5 diff -r e2656c21b42a700f57723b646b621fc7e06a6e5a -r 394c799c92a2450ebc94ea04a006f0875fc0f89a pytest_pep8.py --- a/pytest_pep8.py +++ b/pytest_pep8.py @@ -4,7 +4,7 @@ import pep8 import os -__version__ = '1.0.5' +__version__ = '1.0.6' HISTKEY = "pep8/mtimes" diff -r e2656c21b42a700f57723b646b621fc7e06a6e5a -r 394c799c92a2450ebc94ea04a006f0875fc0f89a setup.py --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ description='pytest plugin to check PEP8 requirements', long_description=open("README.txt").read(), license="MIT license", - version='1.0.5', + version='1.0.6', author='Holger Krekel and Ronny Pfannschmidt', author_email='holger.krekel at gmail.com', url='http://bitbucket.org/hpk42/pytest-pep8/', https://bitbucket.org/hpk42/pytest-pep8/commits/fb3ba1fcb903/ Changeset: fb3ba1fcb903 User: hpk42 Date: 2014-04-27 08:21:42 Summary: Added tag 1.0.6 for changeset 394c799c92a2 Affected #: 1 file diff -r 394c799c92a2450ebc94ea04a006f0875fc0f89a -r fb3ba1fcb9030a42efb84900b11ccfb6050a04f4 .hgtags --- a/.hgtags +++ b/.hgtags @@ -9,3 +9,4 @@ f8a92d028dcd09ecd737d46b62585ef89571aa4d 1.0.3 93ebd08f41f93143242b7088a93673e751234a11 1.0.4 84c788017bac1ebaadc0d3dea47d59bc9dd75e21 1.0.5 +394c799c92a2450ebc94ea04a006f0875fc0f89a 1.0.6 Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:46:51 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:46:51 -0000 Subject: [Pytest-commit] commit/tox: RonnyPfannschmidt: Close branch fix_tox_envlist Message-ID: <20140427174651.10014.65519@app06.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/c08fb2cfa918/ Changeset: c08fb2cfa918 Branch: fix_tox_envlist User: RonnyPfannschmidt Date: 2014-04-27 19:46:48 Summary: Close branch fix_tox_envlist Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:46:51 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:46:51 -0000 Subject: [Pytest-commit] commit/tox: RonnyPfannschmidt: Merged in msabramo/tox/fix_tox_envlist (pull request #100) Message-ID: <20140427174651.3310.79875@app13.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/5646e78d05b2/ Changeset: 5646e78d05b2 User: RonnyPfannschmidt Date: 2014-04-27 19:46:48 Summary: Merged in msabramo/tox/fix_tox_envlist (pull request #100) tox.ini: Fix envlist Affected #: 1 file diff -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 -r 5646e78d05b27b3c7f310bbcef29f765d06a9942 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,py33,py34,pypy +envlist=py27,py26,py34,py33,py32,pypy [testenv:X] commands=echo {posargs} Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:46:51 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:46:51 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427174651.17977.67977@app10.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/f35e2b411c34/ Changeset: f35e2b411c34 Branch: fix_tox_envlist User: Marc Abramowitz Date: 2014-04-27 18:24:33 Summary: tox.ini: Fix envlist It was listing py33 twice and was in no discernible order. Affected #: 1 file diff -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 -r f35e2b411c34c25a3c0d0d13b2bf22e072c95b21 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,py33,py34,pypy +envlist=py27,py26,py34,py33,py32,pypy [testenv:X] commands=echo {posargs} https://bitbucket.org/hpk42/tox/commits/5646e78d05b2/ Changeset: 5646e78d05b2 User: RonnyPfannschmidt Date: 2014-04-27 19:46:48 Summary: Merged in msabramo/tox/fix_tox_envlist (pull request #100) tox.ini: Fix envlist Affected #: 1 file diff -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 -r 5646e78d05b27b3c7f310bbcef29f765d06a9942 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py33,py26,py32,py33,py34,pypy +envlist=py27,py26,py34,py33,py32,pypy [testenv:X] commands=echo {posargs} Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:48:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:48:10 -0000 Subject: [Pytest-commit] commit/tox: RonnyPfannschmidt: Merged in msabramo/tox/fix_test_quickstart (pull request #99) Message-ID: <20140427174810.10014.84396@app06.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/6f9368b8b63e/ Changeset: 6f9368b8b63e User: RonnyPfannschmidt Date: 2014-04-27 19:48:07 Summary: Merged in msabramo/tox/fix_test_quickstart (pull request #99) Fix test_quickstart failures Affected #: 1 file diff -r 5646e78d05b27b3c7f310bbcef29f765d06a9942 -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c tests/test_quickstart.py --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -28,7 +28,7 @@ monkeypatch.setattr( tox._quickstart, 'term_input', self.get_mock_term_input( - ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) + ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) tox._quickstart.main(argv=['tox-quickstart']) @@ -39,7 +39,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -52,7 +52,7 @@ def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'nosetests', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -64,7 +64,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = nosetests @@ -77,7 +77,7 @@ def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'trial', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -89,7 +89,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = trial @@ -102,7 +102,7 @@ def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -113,7 +113,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -185,7 +185,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = py.test @@ -198,7 +198,7 @@ def test_quickstart_main_choose_individual_pythons_and_defaults(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', ''])) + self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -209,7 +209,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -239,7 +239,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -257,6 +257,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'py.test', 'deps': 'pytest', @@ -268,7 +269,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -339,6 +340,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'nosetests -v', 'deps': 'nose', @@ -350,7 +352,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py32, py33, pypy +envlist = py27, py32, py33, py34, pypy [testenv] commands = nosetests -v Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:48:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:48:10 -0000 Subject: [Pytest-commit] commit/tox: RonnyPfannschmidt: Close branch fix_test_quickstart Message-ID: <20140427174810.1520.3349@app07.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/cf80fe7d4c53/ Changeset: cf80fe7d4c53 Branch: fix_test_quickstart User: RonnyPfannschmidt Date: 2014-04-27 19:48:07 Summary: Close branch fix_test_quickstart Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:48:10 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:48:10 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20140427174810.15840.28497@app08.ash-private.bitbucket.org> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/8bbdee883fb8/ Changeset: 8bbdee883fb8 Branch: fix_test_quickstart User: Marc Abramowitz Date: 2014-04-27 10:50:53 Summary: Fix test_quickstart failures that were introduced by adding py34 to tox-quickstart in 94842aabbbcc Affected #: 1 file diff -r 6cda25d37c50bc599c6fd9b55652802b3beffde1 -r 8bbdee883fb85bf2f4ad39c98f540d5ed48b4e71 tests/test_quickstart.py --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -28,7 +28,7 @@ monkeypatch.setattr( tox._quickstart, 'term_input', self.get_mock_term_input( - ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) + ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) tox._quickstart.main(argv=['tox-quickstart']) @@ -39,7 +39,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -52,7 +52,7 @@ def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'nosetests', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -64,7 +64,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = nosetests @@ -77,7 +77,7 @@ def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'trial', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -89,7 +89,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = trial @@ -102,7 +102,7 @@ def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -113,7 +113,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -185,7 +185,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = py.test @@ -198,7 +198,7 @@ def test_quickstart_main_choose_individual_pythons_and_defaults(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', ''])) + self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -209,7 +209,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -239,7 +239,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -257,6 +257,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'py.test', 'deps': 'pytest', @@ -268,7 +269,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -339,6 +340,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'nosetests -v', 'deps': 'nose', @@ -350,7 +352,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py32, py33, pypy +envlist = py27, py32, py33, py34, pypy [testenv] commands = nosetests -v https://bitbucket.org/hpk42/tox/commits/6f9368b8b63e/ Changeset: 6f9368b8b63e User: RonnyPfannschmidt Date: 2014-04-27 19:48:07 Summary: Merged in msabramo/tox/fix_test_quickstart (pull request #99) Fix test_quickstart failures Affected #: 1 file diff -r 5646e78d05b27b3c7f310bbcef29f765d06a9942 -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c tests/test_quickstart.py --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -28,7 +28,7 @@ monkeypatch.setattr( tox._quickstart, 'term_input', self.get_mock_term_input( - ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) + ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest'])) tox._quickstart.main(argv=['tox-quickstart']) @@ -39,7 +39,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -52,7 +52,7 @@ def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'nosetests', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -64,7 +64,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = nosetests @@ -77,7 +77,7 @@ def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'trial', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -89,7 +89,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = trial @@ -102,7 +102,7 @@ def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', + self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -113,7 +113,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -185,7 +185,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = py.test @@ -198,7 +198,7 @@ def test_quickstart_main_choose_individual_pythons_and_defaults(self, monkeypatch): monkeypatch.setattr( tox._quickstart, 'term_input', - self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', ''])) + self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', ''])) tox._quickstart.main(argv=['tox-quickstart']) @@ -209,7 +209,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -239,7 +239,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy, jython +envlist = py26, py27, py32, py33, py34, pypy, jython [testenv] commands = {envpython} setup.py test @@ -257,6 +257,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'py.test', 'deps': 'pytest', @@ -268,7 +269,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py32, py33, pypy +envlist = py26, py27, py32, py33, py34, pypy [testenv] commands = py.test @@ -339,6 +340,7 @@ 'py27': True, 'py32': True, 'py33': True, + 'py34': True, 'pypy': True, 'commands': 'nosetests -v', 'deps': 'nose', @@ -350,7 +352,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py32, py33, pypy +envlist = py27, py32, py33, py34, pypy [testenv] commands = nosetests -v Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:51:05 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:51:05 -0000 Subject: [Pytest-commit] commit/pytest-pep8: hpk42: Merged in saulshanabrook/pytest-pep8/saulshanabrook/add-trove-classifiers-to-show-python-ver-1398620386732 (pull request #7) Message-ID: <20140427175105.13357.38204@app17.ash-private.bitbucket.org> 1 new commit in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/commits/a1a7003e215b/ Changeset: a1a7003e215b User: hpk42 Date: 2014-04-27 19:51:02 Summary: Merged in saulshanabrook/pytest-pep8/saulshanabrook/add-trove-classifiers-to-show-python-ver-1398620386732 (pull request #7) Add trove classifiers to show python version compat Affected #: 1 file diff -r fb3ba1fcb9030a42efb84900b11ccfb6050a04f4 -r a1a7003e215bf996d68f7d925cd655b83a0ceab8 setup.py --- a/setup.py +++ b/setup.py @@ -13,4 +13,11 @@ py_modules=['pytest_pep8'], entry_points={'pytest11': ['pep8 = pytest_pep8']}, install_requires=['pytest-cache', 'pytest>=2.4.2', 'pep8>=1.3', ], + classifiers=[ + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4' + ], ) Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Apr 27 19:51:05 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 27 Apr 2014 17:51:05 -0000 Subject: [Pytest-commit] commit/pytest-pep8: 2 new changesets Message-ID: <20140427175105.12215.76352@app15.ash-private.bitbucket.org> 2 new commits in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/commits/5800f22ff23e/ Changeset: 5800f22ff23e Branch: saulshanabrook/add-trove-classifiers-to-show-python-ver-1398620386732 User: saulshanabrook Date: 2014-04-27 19:39:47 Summary: Add trove classifiers to show python version compat Affected #: 1 file diff -r fb3ba1fcb9030a42efb84900b11ccfb6050a04f4 -r 5800f22ff23e2733f9fab61a6b4768ae6cfda505 setup.py --- a/setup.py +++ b/setup.py @@ -13,4 +13,11 @@ py_modules=['pytest_pep8'], entry_points={'pytest11': ['pep8 = pytest_pep8']}, install_requires=['pytest-cache', 'pytest>=2.4.2', 'pep8>=1.3', ], + classifiers=[ + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4' + ], ) https://bitbucket.org/hpk42/pytest-pep8/commits/a1a7003e215b/ Changeset: a1a7003e215b User: hpk42 Date: 2014-04-27 19:51:02 Summary: Merged in saulshanabrook/pytest-pep8/saulshanabrook/add-trove-classifiers-to-show-python-ver-1398620386732 (pull request #7) Add trove classifiers to show python version compat Affected #: 1 file diff -r fb3ba1fcb9030a42efb84900b11ccfb6050a04f4 -r a1a7003e215bf996d68f7d925cd655b83a0ceab8 setup.py --- a/setup.py +++ b/setup.py @@ -13,4 +13,11 @@ py_modules=['pytest_pep8'], entry_points={'pytest11': ['pep8 = pytest_pep8']}, install_requires=['pytest-cache', 'pytest>=2.4.2', 'pep8>=1.3', ], + classifiers=[ + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4' + ], ) Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 28 09:54:46 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 28 Apr 2014 07:54:46 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Close branch tox_ini_add_flakes_target_3 Message-ID: <20140428075446.17505.80415@app05.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/0bde778e9e0d/ Changeset: 0bde778e9e0d Branch: tox_ini_add_flakes_target_3 User: hpk42 Date: 2014-04-28 09:54:33 Summary: Close branch tox_ini_add_flakes_target_3 Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 28 09:54:46 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 28 Apr 2014 07:54:46 -0000 Subject: [Pytest-commit] commit/tox: hpk42: Merged in msabramo/tox/tox_ini_add_flakes_target_3 (pull request #101) Message-ID: <20140428075446.3952.75203@app17.ash-private.bitbucket.org> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/9c19ff552a68/ Changeset: 9c19ff552a68 User: hpk42 Date: 2014-04-28 09:54:33 Summary: Merged in msabramo/tox/tox_ini_add_flakes_target_3 (pull request #101) Add tox "flakes" target and fix "flakes" errors Affected #: 12 files diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/conftest.py --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,2 +1,2 @@ -from tox._pytestplugin import * +from tox._pytestplugin import * # noqa diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,12 +1,11 @@ -import tox -import pytest -import os, sys -import subprocess +import sys from textwrap import dedent import py +import pytest +import tox import tox._config -from tox._config import * +from tox._config import * # noqa from tox._config import _split_env @@ -110,7 +109,6 @@ def test_defaults_distshare(self, tmpdir, newconfig): config = newconfig([], "") - envconfig = config.envconfigs['python'] assert config.distshare == config.homedir.join(".tox", "distshare") def test_defaults_changed_dir(self, tmpdir, newconfig): @@ -168,6 +166,7 @@ key2={xyz} """) reader = IniReader(config._cfg, fallbacksections=['mydefault']) + assert reader is not None py.test.raises(tox.exception.ConfigError, 'reader.getdefault("mydefault", "key2")') diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_interpreters.py --- a/tests/test_interpreters.py +++ b/tests/test_interpreters.py @@ -2,7 +2,7 @@ import os import pytest -from tox.interpreters import * +from tox.interpreters import * # noqa @pytest.fixture def interpreters(): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -3,7 +3,7 @@ import pytest import os, sys import tox._config -from tox._venv import * +from tox._venv import * # noqa #def test_global_virtualenv(capfd): # v = VirtualEnv() @@ -277,7 +277,7 @@ venv = mocksession.getenv('python') venv.test() mocksession.report.expect("warning", "*test command found but not*") - assert venv.status == "commands failed" + assert venv.status == 0 def test_install_command_whitelisted(newmocksession, monkeypatch): mocksession = newmocksession(['--recreate'], """ @@ -295,7 +295,7 @@ assert venv.status == "commands failed" @pytest.mark.skipif("not sys.platform.startswith('linux')") -def test_install_command_not_installed(newmocksession): +def test_install_command_not_installed_bash(newmocksession): mocksession = newmocksession(['--recreate'], """ [testenv] commands= @@ -387,7 +387,7 @@ [testenv] deps={distshare}/xyz-* """) - xyz = config.distshare.ensure("xyz-1.2.0.zip") + config.distshare.ensure("xyz-1.2.0.zip") xyz2 = config.distshare.ensure("xyz-1.2.1.zip") envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) @@ -507,7 +507,6 @@ l = mocksession._pcalls assert len(l) == 2 for x in l: - args = x.args env = x.env assert env is not None assert 'ENV_VAR' in env @@ -585,6 +584,7 @@ mocksession.report.not_expect("warning", "*test command found but not*") monkeypatch.setenv("PATH", str(tmpdir)) x4 = venv.getcommandpath("x", cwd=tmpdir) + assert x4.endswith('/x') mocksession.report.expect("warning", "*test command found but not*") def test_sethome_only_on_option(newmocksession, monkeypatch): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_z_cmdline.py --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -1,7 +1,6 @@ import tox import py import pytest -import sys from tox._pytestplugin import ReportExpectMock try: import json @@ -129,7 +128,6 @@ }) config = parseconfig([]) session = Session(config) - envlist = ['hello', 'world'] envs = session.venvlist assert len(envs) == 2 env1, env2 = envs @@ -582,6 +580,7 @@ l = distshare.listdir() assert len(l) == 1 sdistfile = l[0] + assert 'pkg123-0.7.zip' in str(sdistfile) def test_separate_sdist(cmd, initproj): distshare = cmd.tmpdir.join("distshare") @@ -611,7 +610,6 @@ distshare=%s sdistsrc={distshare}/pkg123-* """ % distshare) - p0 = distshare.ensure("pkg123-1.3.5.zip") p = distshare.ensure("pkg123-1.4.5.zip") distshare.ensure("pkg123-1.4.5a1.zip") session = Session(config) diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py26,py34,py33,py32,pypy +envlist=py27,py26,py34,py33,py32,pypy,flakes [testenv:X] commands=echo {posargs} @@ -18,6 +18,10 @@ --junitxml={envlogdir}/junit-{envname}.xml \ check_sphinx.py {posargs} +[testenv:flakes] +deps = pytest-flakes>=0.2 +commands = py.test --flakes -m flakes tox tests + [testenv:py25] setenv= PIP_INSECURE=1 diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -20,4 +20,4 @@ class MissingDependency(Error): """ a dependency could not be found or determined. """ -from tox._cmdline import main as cmdline +from tox._cmdline import main as cmdline # noqa diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_cmdline.py --- a/tox/_cmdline.py +++ b/tox/_cmdline.py @@ -79,7 +79,6 @@ return f def popen(self, args, cwd=None, env=None, redirect=True, returnout=False): - logged_command = "%s$ %s" %(cwd, " ".join(map(str, args))) f = outpath = None resultjson = self.session.config.option.resultjson if resultjson or redirect: @@ -411,7 +410,8 @@ sdist_path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] - self.report.error("FAIL could not package project") + self.report.error("FAIL could not package project - v = %r" % + v) return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_config.py --- a/tox/_config.py +++ b/tox/_config.py @@ -1,13 +1,10 @@ import argparse -import distutils.sysconfig import os import random import sys import re import shlex import string -import subprocess -import textwrap import pkg_resources from tox.interpreters import Interpreters @@ -205,7 +202,7 @@ class parseini: def __init__(self, config, inipath): config.toxinipath = inipath - config.toxinidir = toxinidir = config.toxinipath.dirpath() + config.toxinidir = config.toxinipath.dirpath() self._cfg = py.iniconfig.IniConfig(config.toxinipath) config._cfg = self._cfg @@ -239,7 +236,7 @@ # determine indexserver dictionary config.indexserver = {'default': IndexServerConfig('default')} prefix = "indexserver" - for line in reader.getlist(toxsection, "indexserver"): + for line in reader.getlist(toxsection, prefix): name, url = map(lambda x: x.strip(), line.split("=", 1)) config.indexserver[name] = IndexServerConfig(name, url) diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -1,6 +1,5 @@ from __future__ import with_statement -import sys, os, re -import subprocess +import sys, os import py import tox from tox._config import DepConfig @@ -121,8 +120,6 @@ """ if action is None: action = self.session.newaction(self, "update") - report = self.session.report - name = self.envconfig.envname rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ rconfig.matches(self._getliveconfig()): @@ -142,7 +139,8 @@ self.install_deps(action) except tox.exception.InvocationError: v = sys.exc_info()[1] - return "could not install deps %s" %(self.envconfig.deps,) + return "could not install deps %s; v = %r" % ( + self.envconfig.deps, v) def _getliveconfig(self): python = self.envconfig._basepython_info.executable diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_verlib.py --- a/tox/_verlib.py +++ b/tox/_verlib.py @@ -8,7 +8,6 @@ """ -import sys import re class IrrationalVersionError(Exception): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/interpreters.py --- a/tox/interpreters.py +++ b/tox/interpreters.py @@ -1,8 +1,6 @@ import sys -import os import py import re -import subprocess import inspect class Interpreters: Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Apr 28 09:54:47 2014 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 28 Apr 2014 07:54:47 -0000 Subject: [Pytest-commit] commit/tox: 3 new changesets Message-ID: <20140428075447.4809.75310@app10.ash-private.bitbucket.org> 3 new commits in tox: https://bitbucket.org/hpk42/tox/commits/f6c1dd5ff880/ Changeset: f6c1dd5ff880 Branch: tox_ini_add_flakes_target_3 User: Marc Abramowitz Date: 2014-04-28 02:42:34 Summary: tox.ini: Add "flakes" target Affected #: 1 file diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py26,py34,py33,py32,pypy +envlist=py27,py26,py34,py33,py32,pypy,flakes [testenv:X] commands=echo {posargs} @@ -18,6 +18,10 @@ --junitxml={envlogdir}/junit-{envname}.xml \ check_sphinx.py {posargs} +[testenv:flakes] +deps = pytest-flakes>=0.2 +commands = py.test --flakes -m flakes tox tests + [testenv:py25] setenv= PIP_INSECURE=1 https://bitbucket.org/hpk42/tox/commits/64976dd01297/ Changeset: 64976dd01297 Branch: tox_ini_add_flakes_target_3 User: Marc Abramowitz Date: 2014-04-28 02:43:12 Summary: Fix "flakes" problems Affected #: 11 files diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tests/conftest.py --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,2 +1,2 @@ -from tox._pytestplugin import * +from tox._pytestplugin import * # noqa diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,12 +1,11 @@ -import tox -import pytest -import os, sys -import subprocess +import sys from textwrap import dedent import py +import pytest +import tox import tox._config -from tox._config import * +from tox._config import * # noqa from tox._config import _split_env @@ -110,7 +109,6 @@ def test_defaults_distshare(self, tmpdir, newconfig): config = newconfig([], "") - envconfig = config.envconfigs['python'] assert config.distshare == config.homedir.join(".tox", "distshare") def test_defaults_changed_dir(self, tmpdir, newconfig): @@ -168,6 +166,7 @@ key2={xyz} """) reader = IniReader(config._cfg, fallbacksections=['mydefault']) + assert reader is not None py.test.raises(tox.exception.ConfigError, 'reader.getdefault("mydefault", "key2")') diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tests/test_interpreters.py --- a/tests/test_interpreters.py +++ b/tests/test_interpreters.py @@ -2,7 +2,7 @@ import os import pytest -from tox.interpreters import * +from tox.interpreters import * # noqa @pytest.fixture def interpreters(): diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -3,7 +3,7 @@ import pytest import os, sys import tox._config -from tox._venv import * +from tox._venv import * # noqa #def test_global_virtualenv(capfd): # v = VirtualEnv() @@ -277,7 +277,7 @@ venv = mocksession.getenv('python') venv.test() mocksession.report.expect("warning", "*test command found but not*") - assert venv.status == "commands failed" + assert venv.status == 0 def test_install_command_whitelisted(newmocksession, monkeypatch): mocksession = newmocksession(['--recreate'], """ @@ -295,7 +295,7 @@ assert venv.status == "commands failed" @pytest.mark.skipif("not sys.platform.startswith('linux')") -def test_install_command_not_installed(newmocksession): +def test_install_command_not_installed_bash(newmocksession): mocksession = newmocksession(['--recreate'], """ [testenv] commands= @@ -387,7 +387,7 @@ [testenv] deps={distshare}/xyz-* """) - xyz = config.distshare.ensure("xyz-1.2.0.zip") + config.distshare.ensure("xyz-1.2.0.zip") xyz2 = config.distshare.ensure("xyz-1.2.1.zip") envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) @@ -507,7 +507,6 @@ l = mocksession._pcalls assert len(l) == 2 for x in l: - args = x.args env = x.env assert env is not None assert 'ENV_VAR' in env @@ -585,6 +584,7 @@ mocksession.report.not_expect("warning", "*test command found but not*") monkeypatch.setenv("PATH", str(tmpdir)) x4 = venv.getcommandpath("x", cwd=tmpdir) + assert x4.endswith('/x') mocksession.report.expect("warning", "*test command found but not*") def test_sethome_only_on_option(newmocksession, monkeypatch): diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tests/test_z_cmdline.py --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -1,7 +1,6 @@ import tox import py import pytest -import sys from tox._pytestplugin import ReportExpectMock try: import json @@ -129,7 +128,6 @@ }) config = parseconfig([]) session = Session(config) - envlist = ['hello', 'world'] envs = session.venvlist assert len(envs) == 2 env1, env2 = envs @@ -582,6 +580,7 @@ l = distshare.listdir() assert len(l) == 1 sdistfile = l[0] + assert 'pkg123-0.7.zip' in str(sdistfile) def test_separate_sdist(cmd, initproj): distshare = cmd.tmpdir.join("distshare") @@ -611,7 +610,6 @@ distshare=%s sdistsrc={distshare}/pkg123-* """ % distshare) - p0 = distshare.ensure("pkg123-1.3.5.zip") p = distshare.ensure("pkg123-1.4.5.zip") distshare.ensure("pkg123-1.4.5a1.zip") session = Session(config) diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -20,4 +20,4 @@ class MissingDependency(Error): """ a dependency could not be found or determined. """ -from tox._cmdline import main as cmdline +from tox._cmdline import main as cmdline # noqa diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/_cmdline.py --- a/tox/_cmdline.py +++ b/tox/_cmdline.py @@ -79,7 +79,6 @@ return f def popen(self, args, cwd=None, env=None, redirect=True, returnout=False): - logged_command = "%s$ %s" %(cwd, " ".join(map(str, args))) f = outpath = None resultjson = self.session.config.option.resultjson if resultjson or redirect: @@ -411,7 +410,8 @@ sdist_path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] - self.report.error("FAIL could not package project") + self.report.error("FAIL could not package project - v = %r" % + v) return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/_config.py --- a/tox/_config.py +++ b/tox/_config.py @@ -1,13 +1,10 @@ import argparse -import distutils.sysconfig import os import random import sys import re import shlex import string -import subprocess -import textwrap import pkg_resources from tox.interpreters import Interpreters @@ -205,7 +202,7 @@ class parseini: def __init__(self, config, inipath): config.toxinipath = inipath - config.toxinidir = toxinidir = config.toxinipath.dirpath() + config.toxinidir = config.toxinipath.dirpath() self._cfg = py.iniconfig.IniConfig(config.toxinipath) config._cfg = self._cfg @@ -239,7 +236,7 @@ # determine indexserver dictionary config.indexserver = {'default': IndexServerConfig('default')} prefix = "indexserver" - for line in reader.getlist(toxsection, "indexserver"): + for line in reader.getlist(toxsection, prefix): name, url = map(lambda x: x.strip(), line.split("=", 1)) config.indexserver[name] = IndexServerConfig(name, url) diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -1,6 +1,5 @@ from __future__ import with_statement -import sys, os, re -import subprocess +import sys, os import py import tox from tox._config import DepConfig @@ -121,8 +120,6 @@ """ if action is None: action = self.session.newaction(self, "update") - report = self.session.report - name = self.envconfig.envname rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ rconfig.matches(self._getliveconfig()): @@ -142,7 +139,8 @@ self.install_deps(action) except tox.exception.InvocationError: v = sys.exc_info()[1] - return "could not install deps %s" %(self.envconfig.deps,) + return "could not install deps %s; v = %r" % ( + self.envconfig.deps, v) def _getliveconfig(self): python = self.envconfig._basepython_info.executable diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/_verlib.py --- a/tox/_verlib.py +++ b/tox/_verlib.py @@ -8,7 +8,6 @@ """ -import sys import re class IrrationalVersionError(Exception): diff -r f6c1dd5ff880dbfcb23b4175c8f7b39f981df3aa -r 64976dd01297bf74cbd2c5fa84f876897d07cfe5 tox/interpreters.py --- a/tox/interpreters.py +++ b/tox/interpreters.py @@ -1,8 +1,6 @@ import sys -import os import py import re -import subprocess import inspect class Interpreters: https://bitbucket.org/hpk42/tox/commits/9c19ff552a68/ Changeset: 9c19ff552a68 User: hpk42 Date: 2014-04-28 09:54:33 Summary: Merged in msabramo/tox/tox_ini_add_flakes_target_3 (pull request #101) Add tox "flakes" target and fix "flakes" errors Affected #: 12 files diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/conftest.py --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,2 +1,2 @@ -from tox._pytestplugin import * +from tox._pytestplugin import * # noqa diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,12 +1,11 @@ -import tox -import pytest -import os, sys -import subprocess +import sys from textwrap import dedent import py +import pytest +import tox import tox._config -from tox._config import * +from tox._config import * # noqa from tox._config import _split_env @@ -110,7 +109,6 @@ def test_defaults_distshare(self, tmpdir, newconfig): config = newconfig([], "") - envconfig = config.envconfigs['python'] assert config.distshare == config.homedir.join(".tox", "distshare") def test_defaults_changed_dir(self, tmpdir, newconfig): @@ -168,6 +166,7 @@ key2={xyz} """) reader = IniReader(config._cfg, fallbacksections=['mydefault']) + assert reader is not None py.test.raises(tox.exception.ConfigError, 'reader.getdefault("mydefault", "key2")') diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_interpreters.py --- a/tests/test_interpreters.py +++ b/tests/test_interpreters.py @@ -2,7 +2,7 @@ import os import pytest -from tox.interpreters import * +from tox.interpreters import * # noqa @pytest.fixture def interpreters(): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -3,7 +3,7 @@ import pytest import os, sys import tox._config -from tox._venv import * +from tox._venv import * # noqa #def test_global_virtualenv(capfd): # v = VirtualEnv() @@ -277,7 +277,7 @@ venv = mocksession.getenv('python') venv.test() mocksession.report.expect("warning", "*test command found but not*") - assert venv.status == "commands failed" + assert venv.status == 0 def test_install_command_whitelisted(newmocksession, monkeypatch): mocksession = newmocksession(['--recreate'], """ @@ -295,7 +295,7 @@ assert venv.status == "commands failed" @pytest.mark.skipif("not sys.platform.startswith('linux')") -def test_install_command_not_installed(newmocksession): +def test_install_command_not_installed_bash(newmocksession): mocksession = newmocksession(['--recreate'], """ [testenv] commands= @@ -387,7 +387,7 @@ [testenv] deps={distshare}/xyz-* """) - xyz = config.distshare.ensure("xyz-1.2.0.zip") + config.distshare.ensure("xyz-1.2.0.zip") xyz2 = config.distshare.ensure("xyz-1.2.1.zip") envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) @@ -507,7 +507,6 @@ l = mocksession._pcalls assert len(l) == 2 for x in l: - args = x.args env = x.env assert env is not None assert 'ENV_VAR' in env @@ -585,6 +584,7 @@ mocksession.report.not_expect("warning", "*test command found but not*") monkeypatch.setenv("PATH", str(tmpdir)) x4 = venv.getcommandpath("x", cwd=tmpdir) + assert x4.endswith('/x') mocksession.report.expect("warning", "*test command found but not*") def test_sethome_only_on_option(newmocksession, monkeypatch): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tests/test_z_cmdline.py --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -1,7 +1,6 @@ import tox import py import pytest -import sys from tox._pytestplugin import ReportExpectMock try: import json @@ -129,7 +128,6 @@ }) config = parseconfig([]) session = Session(config) - envlist = ['hello', 'world'] envs = session.venvlist assert len(envs) == 2 env1, env2 = envs @@ -582,6 +580,7 @@ l = distshare.listdir() assert len(l) == 1 sdistfile = l[0] + assert 'pkg123-0.7.zip' in str(sdistfile) def test_separate_sdist(cmd, initproj): distshare = cmd.tmpdir.join("distshare") @@ -611,7 +610,6 @@ distshare=%s sdistsrc={distshare}/pkg123-* """ % distshare) - p0 = distshare.ensure("pkg123-1.3.5.zip") p = distshare.ensure("pkg123-1.4.5.zip") distshare.ensure("pkg123-1.4.5a1.zip") session = Session(config) diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py27,py26,py34,py33,py32,pypy +envlist=py27,py26,py34,py33,py32,pypy,flakes [testenv:X] commands=echo {posargs} @@ -18,6 +18,10 @@ --junitxml={envlogdir}/junit-{envname}.xml \ check_sphinx.py {posargs} +[testenv:flakes] +deps = pytest-flakes>=0.2 +commands = py.test --flakes -m flakes tox tests + [testenv:py25] setenv= PIP_INSECURE=1 diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -20,4 +20,4 @@ class MissingDependency(Error): """ a dependency could not be found or determined. """ -from tox._cmdline import main as cmdline +from tox._cmdline import main as cmdline # noqa diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_cmdline.py --- a/tox/_cmdline.py +++ b/tox/_cmdline.py @@ -79,7 +79,6 @@ return f def popen(self, args, cwd=None, env=None, redirect=True, returnout=False): - logged_command = "%s$ %s" %(cwd, " ".join(map(str, args))) f = outpath = None resultjson = self.session.config.option.resultjson if resultjson or redirect: @@ -411,7 +410,8 @@ sdist_path = self._makesdist() except tox.exception.InvocationError: v = sys.exc_info()[1] - self.report.error("FAIL could not package project") + self.report.error("FAIL could not package project - v = %r" % + v) return sdistfile = self.config.distshare.join(sdist_path.basename) if sdistfile != sdist_path: diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_config.py --- a/tox/_config.py +++ b/tox/_config.py @@ -1,13 +1,10 @@ import argparse -import distutils.sysconfig import os import random import sys import re import shlex import string -import subprocess -import textwrap import pkg_resources from tox.interpreters import Interpreters @@ -205,7 +202,7 @@ class parseini: def __init__(self, config, inipath): config.toxinipath = inipath - config.toxinidir = toxinidir = config.toxinipath.dirpath() + config.toxinidir = config.toxinipath.dirpath() self._cfg = py.iniconfig.IniConfig(config.toxinipath) config._cfg = self._cfg @@ -239,7 +236,7 @@ # determine indexserver dictionary config.indexserver = {'default': IndexServerConfig('default')} prefix = "indexserver" - for line in reader.getlist(toxsection, "indexserver"): + for line in reader.getlist(toxsection, prefix): name, url = map(lambda x: x.strip(), line.split("=", 1)) config.indexserver[name] = IndexServerConfig(name, url) diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -1,6 +1,5 @@ from __future__ import with_statement -import sys, os, re -import subprocess +import sys, os import py import tox from tox._config import DepConfig @@ -121,8 +120,6 @@ """ if action is None: action = self.session.newaction(self, "update") - report = self.session.report - name = self.envconfig.envname rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ rconfig.matches(self._getliveconfig()): @@ -142,7 +139,8 @@ self.install_deps(action) except tox.exception.InvocationError: v = sys.exc_info()[1] - return "could not install deps %s" %(self.envconfig.deps,) + return "could not install deps %s; v = %r" % ( + self.envconfig.deps, v) def _getliveconfig(self): python = self.envconfig._basepython_info.executable diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/_verlib.py --- a/tox/_verlib.py +++ b/tox/_verlib.py @@ -8,7 +8,6 @@ """ -import sys import re class IrrationalVersionError(Exception): diff -r 6f9368b8b63e8f1f150abc6ccdfaa113fcc05a2c -r 9c19ff552a68d173d0fa3146d4e1329d8b6b2012 tox/interpreters.py --- a/tox/interpreters.py +++ b/tox/interpreters.py @@ -1,8 +1,6 @@ import sys -import os import py import re -import subprocess import inspect class Interpreters: Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.