2 new commits in pytest:
https://bitbucket.org/hpk42/pytest/changeset/e4b9018a524e/ changeset: e4b9018a524e user: hpk42 date: 2011-12-02 22:00:19 summary: fix issue93 - avoid "delayed" teardowns for distributed testing by simplifying handling of teardowns. affected #: 8 files diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +Changes between 2.2.0 and 2.2.1.dev +---------------------------------------- + +- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns": + the final test in a test node will now run its teardown directly + instead of waiting for the end of the session. Thanks Dave Hunt for + the good reporting and feedback. The pytest_runtest_protocol as well + as the pytest_runtest_teardown hooks now have "nextitem" available + which will be None indicating the end of the test run. + Changes between 2.1.3 and 2.2.0 ---------------------------------------- diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.0' +__version__ = '2.2.1.dev1' diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b _pytest/hookspec.py --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -121,16 +121,23 @@ def pytest_itemstart(item, node=None): """ (deprecated, use pytest_runtest_logstart). """ -def pytest_runtest_protocol(item): - """ implements the standard runtest_setup/call/teardown protocol including - capturing exceptions and calling reporting hooks on the results accordingly. +def pytest_runtest_protocol(item, nextitem): + """ implements the runtest_setup/call/teardown protocol for + the given test item, including capturing exceptions and calling + reporting hooks. + + :arg item: test item for which the runtest protocol is performed. + + :arg nexitem: the scheduled-to-be-next test item (or None if this + is the end my friend). This argument is passed on to + :py:func:`pytest_runtest_teardown`. :return boolean: True if no further hook implementations should be invoked. """ pytest_runtest_protocol.firstresult = True def pytest_runtest_logstart(nodeid, location): - """ signal the start of a test run. """ + """ signal the start of running a single test item. """ def pytest_runtest_setup(item): """ called before ``pytest_runtest_call(item)``. """ @@ -138,8 +145,14 @@ def pytest_runtest_call(item): """ called to execute the test ``item``. """ -def pytest_runtest_teardown(item): - """ called after ``pytest_runtest_call``. """ +def pytest_runtest_teardown(item, nextitem): + """ called after ``pytest_runtest_call``. + + :arg nexitem: the scheduled-to-be-next test item (None if no further + test item is scheduled). This argument can be used to + perform exact teardowns, i.e. calling just enough finalizers + so that nextitem only needs to call setup-functions. + """ def pytest_runtest_makereport(item, call): """ return a :py:class:`_pytest.runner.TestReport` object diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -106,8 +106,12 @@ def pytest_runtestloop(session): if session.config.option.collectonly: return True - for item in session.items: - item.config.hook.pytest_runtest_protocol(item=item) + for i, item in enumerate(session.items): + try: + nextitem = session.items[i+1] + except IndexError: + nextitem = None + item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) if session.shouldstop: raise session.Interrupted(session.shouldstop) return True diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -59,33 +59,20 @@ def __init__(self, location): self.location = location -def perform_pending_teardown(config, nextitem): - try: - olditem, log = config._pendingteardown - except AttributeError: - pass - else: - del config._pendingteardown - olditem.nextitem = nextitem - call_and_report(olditem, "teardown", log) - -def pytest_runtest_protocol(item): - perform_pending_teardown(item.config, item) +def pytest_runtest_protocol(item, nextitem): item.ihook.pytest_runtest_logstart( nodeid=item.nodeid, location=item.location, ) - runtestprotocol(item, teardowndelayed=True) + runtestprotocol(item, nextitem=nextitem) return True -def runtestprotocol(item, log=True, teardowndelayed=False): +def runtestprotocol(item, log=True, nextitem=None): rep = call_and_report(item, "setup", log) reports = [rep] if rep.passed: reports.append(call_and_report(item, "call", log)) - if teardowndelayed: - item.config._pendingteardown = item, log - else: - reports.append(call_and_report(item, "teardown", log)) + reports.append(call_and_report(item, "teardown", log, + nextitem=nextitem)) return reports def pytest_runtest_setup(item): @@ -94,17 +81,8 @@ def pytest_runtest_call(item): item.runtest() -def pytest_runtest_teardown(item): - item.session._setupstate.teardown_exact(item) - -def pytest__teardown_final(session): - perform_pending_teardown(session.config, None) - #call = CallInfo(session._setupstate.teardown_all, when="teardown") - #if call.excinfo: - # ntraceback = call.excinfo.traceback .cut(excludepath=py._pydir) - # call.excinfo.traceback = ntraceback.filter() - # longrepr = call.excinfo.getrepr(funcargs=True) - # return TeardownErrorReport(longrepr) +def pytest_runtest_teardown(item, nextitem): + item.session._setupstate.teardown_exact(item, nextitem) def pytest_report_teststatus(report): if report.when in ("setup", "teardown"): @@ -120,18 +98,18 @@ # # Implementation -def call_and_report(item, when, log=True): - call = call_runtest_hook(item, when) +def call_and_report(item, when, log=True, **kwds): + call = call_runtest_hook(item, when, **kwds) hook = item.ihook report = hook.pytest_runtest_makereport(item=item, call=call) if log: hook.pytest_runtest_logreport(report=report) return report -def call_runtest_hook(item, when): +def call_runtest_hook(item, when, **kwds): hookname = "pytest_runtest_" + when ihook = getattr(item.ihook, hookname) - return CallInfo(lambda: ihook(item=item), when=when) + return CallInfo(lambda: ihook(item=item, **kwds), when=when) class CallInfo: """ Result/Exception info a function invocation. """ @@ -338,9 +316,8 @@ self._teardown_with_finalization(None) assert not self._finalizers - def teardown_exact(self, item): - colitem = item.nextitem - needed_collectors = colitem and colitem.listchain() or [] + def teardown_exact(self, item, nextitem): + needed_collectors = nextitem and nextitem.listchain() or [] self._teardown_towards(needed_collectors) def _teardown_towards(self, needed_collectors): diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.2.0', + version='2.2.1.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -70,4 +70,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() + main() \ No newline at end of file diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -689,7 +689,7 @@ teardownlist = item.getparent(pytest.Module).obj.teardownlist ss = item.session._setupstate assert not teardownlist - ss.teardown_exact(item) + ss.teardown_exact(item, None) print(ss.stack) assert teardownlist == [1] diff -r 4a015a903b1650c15fed3c8283f67f8c46271127 -r e4b9018a524e8176ae3a2970cb5cc84342b9188b testing/test_runner.py --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -30,9 +30,9 @@ def test_teardown_exact_stack_empty(self, testdir): item = testdir.getitem("def test_func(): pass") ss = runner.SetupState() - ss.teardown_exact(item) - ss.teardown_exact(item) - ss.teardown_exact(item) + ss.teardown_exact(item, None) + ss.teardown_exact(item, None) + ss.teardown_exact(item, None) def test_setup_fails_and_failure_is_cached(self, testdir): item = testdir.getitem(""" https://bitbucket.org/hpk42/pytest/changeset/ff737e56f188/ changeset: ff737e56f188 user: hpk42 date: 2011-12-02 22:00:21 summary: yay! now that we have perfect teardowns we don't need some ugly internal hooks anymore. affected #: 6 files diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.2.1.dev1' +__version__ = '2.2.1.dev2' diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 _pytest/hookspec.py --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -165,14 +165,6 @@ """ process a test setup/call/teardown report relating to the respective phase of executing a test. """ -# special handling for final teardown - somewhat internal for now -def pytest__teardown_final(session): - """ called before test session finishes. """ -pytest__teardown_final.firstresult = True - -def pytest__teardown_final_logerror(report, session): - """ called if runtest_teardown_final failed. """ - # ------------------------------------------------------------------------- # test session related hooks # ------------------------------------------------------------------------- diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -48,13 +48,6 @@ def pytest_sessionstart(session): session._setupstate = SetupState() -def pytest_sessionfinish(session, exitstatus): - hook = session.config.hook - rep = hook.pytest__teardown_final(session=session) - if rep: - hook.pytest__teardown_final_logerror(session=session, report=rep) - session.exitstatus = 1 - class NodeInfo: def __init__(self, location): self.location = location diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -161,9 +161,6 @@ def pytest_deselected(self, items): self.stats.setdefault('deselected', []).extend(items) - def pytest__teardown_final_logerror(self, report): - self.stats.setdefault("error", []).append(report) - def pytest_runtest_logstart(self, nodeid, location): # ensure that the path is printed before the # 1st test of a module starts running diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.2.1.dev1', + version='2.2.1.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r e4b9018a524e8176ae3a2970cb5cc84342b9188b -r ff737e56f1881a9bd7169b145b39fdb26052e421 testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -205,7 +205,7 @@ #"*1 fixture failure*" ]) - def test_teardown_final_capturing(self, testdir): + def test_teardown_capturing_final(self, testdir): p = testdir.makepyfile(""" def teardown_module(mod): print ("teardown module") 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. _______________________________________________ py-svn mailing list py-svn@codespeak.net http://codespeak.net/mailman/listinfo/py-svn