From 3d18c9c1c6e58e30da97e74fc79efb91f1aae24e Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 18 Dec 2016 21:30:00 +0000 Subject: [PATCH 01/35] 'xfail' markers without a condition no longer rely on the underlying `Item` deriving from `PyobjMixin` --- _pytest/skipping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index a8eaea98a..cfaed2fa7 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -119,7 +119,6 @@ class MarkEvaluator: if hasattr(self, 'result'): return self.result if self.holder: - d = self._getglobals() if self.holder.args or 'condition' in self.holder.kwargs: self.result = False # "holder" might be a MarkInfo or a MarkDecorator; only @@ -135,6 +134,7 @@ class MarkEvaluator: for expr in args: self.expr = expr if isinstance(expr, py.builtin._basestring): + d = self._getglobals() result = cached_eval(self.item.config, expr, d) else: if "reason" not in kwargs: From 8db9915374f60d00780b758b98ada8d3f80af9bb Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Sun, 18 Dec 2016 21:39:35 +0000 Subject: [PATCH 02/35] Update AUTHORS, CHANGELOG --- AUTHORS | 1 + CHANGELOG.rst | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 8c6fa1b5d..3999db9b3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,6 +16,7 @@ Antony Lee Armin Rigo Aron Curzon Aviv Palivoda +Barney Gale Ben Webb Benjamin Peterson Bernard Pratz diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7c3df0327..64453f259 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,10 @@ 3.0.6.dev0 (unreleased) ======================= -* +* Conditionless ``xfail`` markers no longer rely on the underlying test item + being an instance of ``PyobjMixin``, and can therefore apply to tests not + collected by the built-in python test collector. Thanks `@barneygale`_ for the + PR. * pytest no longer recognizes coroutine functions as yield tests (`#2129`_). Thanks to `@malinoff`_ for the PR. From df409a0c0ee9948d3725ee79c4a0a91960466af7 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Mon, 2 Jan 2017 22:01:40 +0000 Subject: [PATCH 03/35] Fix CHANGELOG.rst --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 64453f259..eca520bbc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,7 @@ * +.. _@barneygale: https://github.com/barneygale .. _@lesteve: https://github.com/lesteve .. _@malinoff: https://github.com/malinoff .. _@pelme: https://github.com/pelme From d1c725078a8c2fccfc573772e1944b9fe2a902e6 Mon Sep 17 00:00:00 2001 From: mbyt Date: Mon, 30 Jan 2017 21:20:12 +0100 Subject: [PATCH 04/35] Allow to skip unittests if --pdb active closes #2137 --- CHANGELOG.rst | 7 ++++++- _pytest/unittest.py | 14 ++++++++++++-- testing/test_pdb.py | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 006c0ed0f..7f24a3282 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,8 @@ 3.0.7 (unreleased) ======================= -* +* Fix regression, pytest now skips unittest correctly if run with ``--pdb`` + (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR. * @@ -9,6 +10,10 @@ * +.. _@gst: https://github.com/gst + +.. _#2137: https://github.com/pytest-dev/pytest/issues/2137 + 3.0.6 (2017-01-22) ================== diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 73224010b..4d303e7e3 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -65,7 +65,6 @@ class UnitTestCase(pytest.Class): yield TestCaseFunction('runTest', parent=self) - class TestCaseFunction(pytest.Function): _excinfo = None @@ -157,9 +156,20 @@ class TestCaseFunction(pytest.Function): self._testcase(result=self) else: # disables tearDown and cleanups for post mortem debugging (see #1890) + # but still implements the skipping machinery (see #2137) + testMethod = getattr(self._testcase, self._testcase._testMethodName) + if (getattr(self._testcase.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)): + # If the class or method was skipped. + skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') + or getattr(testMethod, '__unittest_skip_why__', '')) + try: + self._testcase._addSkip(self, self._testcase, skip_why) + except TypeError: # PY2 + self._testcase._addSkip(self, skip_why) + return self._testcase.debug() - def _prunetraceback(self, excinfo): pytest.Function._prunetraceback(self, excinfo) traceback = excinfo.traceback.filter( diff --git a/testing/test_pdb.py b/testing/test_pdb.py index df58dad87..8a2efdb87 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -106,6 +106,20 @@ class TestPDB: assert 'debug.me' in rest self.flush(child) + def test_pdb_unittest_skip(self, testdir): + p1 = testdir.makepyfile(""" + import unittest + @unittest.skipIf(True, 'Skipping also with pdb active') + class MyTestCase(unittest.TestCase): + def test_one(self): + assert 0 + """) + child = testdir.spawn_pytest("-rs --pdb %s" % p1) + child.expect('Skipping also with pdb active') + child.expect('1 skipped in') + child.sendeof() + self.flush(child) + def test_pdb_interaction_capture(self, testdir): p1 = testdir.makepyfile(""" def test_1(): From 36b6f17727589defa28101142009410c9c771289 Mon Sep 17 00:00:00 2001 From: mbyt Date: Tue, 31 Jan 2017 21:03:49 +0100 Subject: [PATCH 05/35] fixing code-style, keep flake8 happy --- _pytest/unittest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 4d303e7e3..0126ff838 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -161,8 +161,8 @@ class TestCaseFunction(pytest.Function): if (getattr(self._testcase.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False)): # If the class or method was skipped. - skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') - or getattr(testMethod, '__unittest_skip_why__', '')) + skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or + getattr(testMethod, '__unittest_skip_why__', '')) try: self._testcase._addSkip(self, self._testcase, skip_why) except TypeError: # PY2 From e1c5314d80ad7b90257bfaf7a807a3d2e10f4494 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 1 Feb 2017 02:37:55 -0200 Subject: [PATCH 06/35] Replace 'raise StopIteration' usages in the code by 'return's in accordance to PEP-479 Fix #2160 --- CHANGELOG.rst | 11 +++++++++-- _pytest/python.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 006c0ed0f..612ee0f3b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,5 +1,10 @@ 3.0.7 (unreleased) -======================= +================== + +* + +* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). + Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. * @@ -7,7 +12,9 @@ * -* +.. _#2160: https://github.com/pytest-dev/pytest/issues/2160 + +.. _PEP-479: https://www.python.org/dev/peps/pep-0479/ 3.0.6 (2017-01-22) diff --git a/_pytest/python.py b/_pytest/python.py index e46f2f1bc..2973d43d6 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -174,7 +174,7 @@ def pytest_pycollect_makeitem(collector, name, obj): outcome = yield res = outcome.get_result() if res is not None: - raise StopIteration + return # nothing was collected elsewhere, let's do it here if isclass(obj): if collector.istestclass(obj, name): From ad56cd8027756a8bc1aa83402bbfffeae3520129 Mon Sep 17 00:00:00 2001 From: mbyt Date: Thu, 2 Feb 2017 05:01:51 +0100 Subject: [PATCH 07/35] extract a _handle_skip method, secure PY2 branch --- _pytest/unittest.py | 30 +++++++++++++++++++----------- testing/test_pdb.py | 1 + 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 0126ff838..34eb9885b 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -151,22 +151,30 @@ class TestCaseFunction(pytest.Function): def stopTest(self, testcase): pass + def _handle_skip(self): + # implements the skipping machinery (see #2137) + # analog to pythons Lib/unittest/case.py:run + testMethod = getattr(self._testcase, self._testcase._testMethodName) + if (getattr(self._testcase.__class__, "__unittest_skip__", False) or + getattr(testMethod, "__unittest_skip__", False)): + # If the class or method was skipped. + skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or + getattr(testMethod, '__unittest_skip_why__', '')) + try: # PY3, unittest2 on PY2 + self._testcase._addSkip(self, self._testcase, skip_why) + except TypeError: # PY2 + if sys.version_info[0] != 2: + raise + self._testcase._addSkip(self, skip_why) + return True + return False + def runtest(self): if self.config.pluginmanager.get_plugin("pdbinvoke") is None: self._testcase(result=self) else: # disables tearDown and cleanups for post mortem debugging (see #1890) - # but still implements the skipping machinery (see #2137) - testMethod = getattr(self._testcase, self._testcase._testMethodName) - if (getattr(self._testcase.__class__, "__unittest_skip__", False) or - getattr(testMethod, "__unittest_skip__", False)): - # If the class or method was skipped. - skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or - getattr(testMethod, '__unittest_skip_why__', '')) - try: - self._testcase._addSkip(self, self._testcase, skip_why) - except TypeError: # PY2 - self._testcase._addSkip(self, skip_why) + if self._handle_skip(): return self._testcase.debug() diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 8a2efdb87..52a75d916 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -107,6 +107,7 @@ class TestPDB: self.flush(child) def test_pdb_unittest_skip(self, testdir): + """Test for issue #2137""" p1 = testdir.makepyfile(""" import unittest @unittest.skipIf(True, 'Skipping also with pdb active') From bad261279c5af15e2126023a03f08389c189b2d5 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 3 Feb 2017 16:04:34 +0100 Subject: [PATCH 08/35] Do not asssume `Item.obj` in 'skipping' plugin See #2231 for discussion. --- _pytest/skipping.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index 91a34169f..591977efd 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -112,7 +112,8 @@ class MarkEvaluator: def _getglobals(self): d = {'os': os, 'sys': sys, 'config': self.item.config} - d.update(self.item.obj.__globals__) + if hasattr(self.item, 'obj'): + d.update(self.item.obj.__globals__) return d def _istrue(self): From 1a88a91c7a04ed996bee3c8957e07d9a0c735de1 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 3 Feb 2017 16:29:43 +0100 Subject: [PATCH 09/35] Update authors/history --- AUTHORS | 1 + CHANGELOG.rst | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index c2400f508..d1a4aa675 100644 --- a/AUTHORS +++ b/AUTHORS @@ -141,5 +141,6 @@ Trevor Bekolay Tyler Goodlet Vasily Kuznetsov Victor Uriarte +Vidar T. Fauske Wouter van Ackooy Xuecong Liao diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 612ee0f3b..b81803324 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,8 @@ * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. -* +* Skipping plugin now also works with test items generated by custom collectors (`#2231`_). + Thanks to `@vidartf`_. * @@ -16,6 +17,10 @@ .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ +.. _#2231: https://github.com/pytest-dev/pytest/issues/2231 + +.. _@vidartf: https://github.com/vidartf + 3.0.6 (2017-01-22) ================== From 832c89dd5fa7c310ae05ddcffc81025bed7a3c14 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Fri, 3 Feb 2017 16:59:30 +0100 Subject: [PATCH 10/35] Test for `pytest.mark.xfail` with non-Python Item --- testing/test_skipping.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 2e7868d3a..ac4412fcb 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -969,3 +969,26 @@ def test_module_level_skip_error(testdir): result.stdout.fnmatch_lines( "*Using pytest.skip outside of a test is not allowed*" ) + + +def test_mark_xfail_item(testdir): + # Ensure pytest.mark.xfail works with non-Python Item + testdir.makeconftest(""" + import pytest + + class MyItem(pytest.Item): + nodeid = 'foo' + def setup(self): + marker = pytest.mark.xfail(True, reason="Expected failure") + self.add_marker(marker) + def runtest(self): + assert False + + def pytest_collect_file(path, parent): + return MyItem("foo", parent) + """) + result = testdir.inline_run() + passed, skipped, failed = result.listoutcomes() + assert not failed + xfailed = [r for r in skipped if hasattr(r, 'wasxfail')] + assert xfailed From 87fb689ab1d05935bfd75ff29fcbc1de4161f606 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 7 Feb 2017 14:00:13 +0200 Subject: [PATCH 11/35] Remove an unneeded `except KeyboardInterrupt` KeyboardInterrupt is a subclass of BaseException, but not of Exception. Hence if we remove this except, KeyboardInterrupts will still be raised so the behavior stays the same. --- _pytest/fixtures.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index 28bcd4d8d..c5bf75386 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -124,8 +124,6 @@ def getfixturemarker(obj): exceptions.""" try: return getattr(obj, "_pytestfixturefunction", None) - except KeyboardInterrupt: - raise except Exception: # some objects raise errors like request (from flask import request) # we don't expect them to be fixture functions From 3a0a0c2df93ecfcf4fb09a0f7c24bf3a06d1bae5 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 5 Feb 2017 23:55:39 +0200 Subject: [PATCH 12/35] Ignore errors raised from descriptors when collecting fixtures Descriptors (e.g. properties) such as in the added test case are triggered during collection, executing arbitrary code which can raise. Previously, such exceptions were propagated and failed the collection. Now these exceptions are caught and the corresponding attributes are silently ignored. A better solution would be to completely skip access to all custom descriptors, such that the offending code doesn't even trigger. However I think this requires manually going through the instance and all of its MRO for each and every attribute checking if it might be a proper fixture before accessing it. So I took the easy route here. In other words, putting something like this in your test class is still a bad idea...: @property def innocent(self): os.system('rm -rf /') Fixes #2234. --- AUTHORS | 1 + CHANGELOG.rst | 7 +++++++ _pytest/fixtures.py | 5 ++++- testing/python/collect.py | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index fa1140d4d..b09516a1d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -118,6 +118,7 @@ Piotr Banaszkiewicz Punyashloka Biswal Quentin Pradet Ralf Schmitt +Ran Benita Raphael Pierzina Raquel Alegre Roberto Polli diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d9ff2afd5..c11200d64 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,9 @@ * +* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_). + Thanks to `@bluetech`_. + * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. @@ -16,6 +19,10 @@ * +.. _#2234: https://github.com/pytest-dev/pytest/issues/2234 + +.. _@bluetech: https://github.com/bluetech + .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index c5bf75386..248708f6e 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -14,6 +14,7 @@ from _pytest.compat import ( getfslineno, get_real_func, is_generator, isclass, getimfunc, getlocation, getfuncargnames, + safe_getattr, ) def pytest_sessionstart(session): @@ -1066,7 +1067,9 @@ class FixtureManager: self._holderobjseen.add(holderobj) autousenames = [] for name in dir(holderobj): - obj = getattr(holderobj, name, None) + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getatt() ignores such exceptions. + obj = safe_getattr(holderobj, name, None) # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) # or are "@pytest.fixture" marked marker = getfixturemarker(obj) diff --git a/testing/python/collect.py b/testing/python/collect.py index 1e69f2da9..cce934ddc 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -166,6 +166,16 @@ class TestClass: "because it has a __new__ constructor*" ) + def test_issue2234_property(self, testdir): + testdir.makepyfile(""" + class TestCase(object): + @property + def prop(self): + raise NotImplementedError() + """) + result = testdir.runpytest() + assert result.ret == EXIT_NOTESTSCOLLECTED + class TestGenerator: def test_generative_functions(self, testdir): From 9eb1d7395107edbb0401eca83a69cb94dbc79308 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Feb 2017 22:39:34 -0200 Subject: [PATCH 13/35] --override-ini now correctly overrides some fundamental options like "python_files" #2238 --- CHANGELOG.rst | 5 ++++- _pytest/config.py | 19 ++++++++++--------- testing/test_config.py | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f33663b2a..a8583d09f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,8 @@ * Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_). Thanks to `@bluetech`_. -* +* ``--override-ini`` now correctly overrides some fundamental options like ``python_files`` (`#2238`_). + Thanks `@sirex`_ for the report and `@nicoddemus`_ for the PR. * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. @@ -28,12 +29,14 @@ .. _@bluetech: https://github.com/bluetech .. _@gst: https://github.com/gst +.. _@sirex: https://github.com/sirex .. _@vidartf: https://github.com/vidartf .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 .. _#2231: https://github.com/pytest-dev/pytest/issues/2231 .. _#2234: https://github.com/pytest-dev/pytest/issues/2234 +.. _#2238: https://github.com/pytest-dev/pytest/issues/2238 .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ diff --git a/_pytest/config.py b/_pytest/config.py index 42d1a118a..c73416f0a 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -877,6 +877,7 @@ class Config(object): self.trace = self.pluginmanager.trace.root.get("config") self.hook = self.pluginmanager.hook self._inicache = {} + self._override_ini = () self._opt2dest = {} self._cleanup = [] self._warn = self.pluginmanager._warn @@ -977,6 +978,7 @@ class Config(object): self.invocation_dir = py.path.local() self._parser.addini('addopts', 'extra command line options', 'args') self._parser.addini('minversion', 'minimally required pytest version') + self._override_ini = ns.override_ini or () def _consider_importhook(self, args, entrypoint_name): """Install the PEP 302 import hook if using assertion re-writing. @@ -1159,15 +1161,14 @@ class Config(object): # and -o foo1=bar1 -o foo2=bar2 options # always use the last item if multiple value set for same ini-name, # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 - if self.getoption("override_ini", None): - for ini_config_list in self.option.override_ini: - for ini_config in ini_config_list: - try: - (key, user_ini_value) = ini_config.split("=", 1) - except ValueError: - raise UsageError("-o/--override-ini expects option=value style.") - if key == name: - value = user_ini_value + for ini_config_list in self._override_ini: + for ini_config in ini_config_list: + try: + (key, user_ini_value) = ini_config.split("=", 1) + except ValueError: + raise UsageError("-o/--override-ini expects option=value style.") + if key == name: + value = user_ini_value return value def getoption(self, name, default=notset, skip=False): diff --git a/testing/test_config.py b/testing/test_config.py index e6aa423e8..3ce51d639 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -778,6 +778,21 @@ class TestOverrideIniArgs: result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s") result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"]) + @pytest.mark.parametrize('with_ini', [True, False]) + def test_override_ini_handled_asap(self, testdir, with_ini): + """-o should be handled as soon as possible and always override what's in ini files (#2238)""" + if with_ini: + testdir.makeini(""" + [pytest] + python_files=test_*.py + """) + testdir.makepyfile(unittest_ini_handle=""" + def test(): + pass + """) + result = testdir.runpytest("--override-ini", 'python_files=unittest_*.py') + result.stdout.fnmatch_lines(["*1 passed in*"]) + def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch): monkeypatch.chdir(str(tmpdir)) a = tmpdir.mkdir("a") From b536fb7ace7ab4da584b942395235e12d962ae36 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Tue, 14 Feb 2017 11:45:39 +0000 Subject: [PATCH 14/35] Mention next training event. --- doc/en/talks.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/en/talks.rst b/doc/en/talks.rst index c35fba0b0..85faf6455 100644 --- a/doc/en/talks.rst +++ b/doc/en/talks.rst @@ -4,7 +4,9 @@ Talks and Tutorials .. sidebar:: Next Open Trainings - `pytest workshop `_, 8th December 2016, Bern, Switzerland + `Professional Testing with Python + `_, + 26-28 April 2017, Leipzig, Germany. .. _`funcargs`: funcargs.html From 58ce3a9e8c48fe557bfdc644a96df234936247e1 Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Tue, 14 Feb 2017 16:54:32 -0800 Subject: [PATCH 15/35] Safer sys.modules delete --- _pytest/assertion/rewrite.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index abf5b491f..7408c4746 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -215,7 +215,8 @@ class AssertionRewritingHook(object): mod.__loader__ = self py.builtin.exec_(co, mod.__dict__) except: - del sys.modules[name] + if name in sys.modules: + del sys.modules[name] raise return sys.modules[name] From 00ec30353b090f8bc5ab36b7a6738f74477df0ba Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Tue, 14 Feb 2017 17:08:42 -0800 Subject: [PATCH 16/35] Update docs as requested --- AUTHORS | 1 + CHANGELOG.rst | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/AUTHORS b/AUTHORS index b09516a1d..b49405bf7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -113,6 +113,7 @@ Nicolas Delaby Oleg Pidsadnyi Oliver Bestwalter Omar Kohl +Patrick Hayes Pieter Mulder Piotr Banaszkiewicz Punyashloka Biswal diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f33663b2a..394c57fa4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,9 @@ 3.0.7 (unreleased) ================== +* Fix issue when trying to import the `anydbm` module. (`#2248`_). + Thanks `@pfhayes`_ for the PR. + * Fix regression, pytest now skips unittest correctly if run with ``--pdb`` (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR. @@ -30,6 +33,7 @@ .. _@gst: https://github.com/gst .. _@vidartf: https://github.com/vidartf +.. _#2248: https://github.com/pytest-dev/pytest/issues/2248 .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 .. _#2231: https://github.com/pytest-dev/pytest/issues/2231 From 49289fed52eb99f3a3258d71e2e6ab24a850658a Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Tue, 14 Feb 2017 17:21:20 -0800 Subject: [PATCH 17/35] Fix docs --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 394c57fa4..f172be1ea 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -29,6 +29,7 @@ * +.. _@pfhayes: https://github.com/pfhayes .. _@bluetech: https://github.com/bluetech .. _@gst: https://github.com/gst .. _@vidartf: https://github.com/vidartf From 6b5566db66300f9a566efecf1d9389842dc9581a Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Tue, 14 Feb 2017 17:47:42 -0800 Subject: [PATCH 18/35] Update changelog --- CHANGELOG.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f172be1ea..23ff64320 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,9 @@ 3.0.7 (unreleased) ================== -* Fix issue when trying to import the `anydbm` module. (`#2248`_). +* Fix issue in assertion rewriting breaking due to modules silently discarding + other modules when importing fails + Notably, importing the `anydbm` module is fixed. (`#2248`_). Thanks `@pfhayes`_ for the PR. * Fix regression, pytest now skips unittest correctly if run with ``--pdb`` From c4d974460ca611cfcd51d05ab742ebb166df4d2a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 15 Feb 2017 11:57:03 -0200 Subject: [PATCH 19/35] Improve pytest_plugins docs As discussed in #2246 --- doc/en/writing_plugins.rst | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 770f81e46..de0aa1390 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -236,20 +236,31 @@ import ``helper.py`` normally. The contents of Requiring/Loading plugins in a test module or conftest file ----------------------------------------------------------- -You can require plugins in a test module or a conftest file like this:: +You can require plugins in a test module or a ``conftest.py`` file like this: - pytest_plugins = "name1", "name2", +.. code-block:: python + + pytest_plugins = ["name1", "name2"] When the test module or conftest plugin is loaded the specified plugins -will be loaded as well. You can also use dotted path like this:: +will be loaded as well. Any module can be blessed as a plugin, including internal +application modules: + +.. code-block:: python pytest_plugins = "myapp.testsupport.myplugin" -which will import the specified module as a ``pytest`` plugin. +``pytest_plugins`` variables are processed recursively, so note that in the example above +if ``myapp.testsupport.myplugin`` also declares ``pytest_plugins``, the contents +of the variable will also be loaded as plugins, and so on. -Plugins imported like this will automatically be marked to require -assertion rewriting using the :func:`pytest.register_assert_rewrite` -mechanism. However for this to have any effect the module must not be +This mechanism makes it easy to share textures within applications or even +external applications without the need to create external plugins using +the ``setuptools``'s entry point technique. + +Plugins imported by ``pytest_plugins`` will also automatically be marked +for assertion rewriting (see :func:`pytest.register_assert_rewrite`). +However for this to have any effect the module must not be imported already; if it was already imported at the time the ``pytest_plugins`` statement is processed, a warning will result and assertions inside the plugin will not be re-written. To fix this you From eeb6603d71737385cea62633d4db28f9e0db269b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 15 Feb 2017 16:54:53 +0200 Subject: [PATCH 20/35] Python 3.6 invalid escape sequence deprecation fixes --- _pytest/pytester.py | 2 +- _pytest/tmpdir.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 6c63bbbbf..977e1f9ad 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -332,7 +332,7 @@ def testdir(request, tmpdir_factory): return Testdir(request, tmpdir_factory) -rex_outcome = re.compile("(\d+) ([\w-]+)") +rex_outcome = re.compile(r"(\d+) ([\w-]+)") class RunResult: """The result of running a command. diff --git a/_pytest/tmpdir.py b/_pytest/tmpdir.py index 28a6b0636..0f878ad01 100644 --- a/_pytest/tmpdir.py +++ b/_pytest/tmpdir.py @@ -116,7 +116,7 @@ def tmpdir(request, tmpdir_factory): path object. """ name = request.node.name - name = re.sub("[\W]", "_", name) + name = re.sub(r"[\W]", "_", name) MAXVAL = 30 if len(name) > MAXVAL: name = name[:MAXVAL] From ede4e9171f3411d71ad280280fcaa45b1e5fc371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 15 Feb 2017 17:00:18 +0200 Subject: [PATCH 21/35] Spelling fixes --- CHANGELOG.rst | 2 +- _pytest/cacheprovider.py | 2 +- _pytest/hookspec.py | 2 +- _pytest/main.py | 2 +- _pytest/mark.py | 2 +- _pytest/pytester.py | 6 +++--- _pytest/python.py | 2 +- _pytest/unittest.py | 2 +- testing/test_assertion.py | 2 +- testing/test_assertrewrite.py | 2 +- testing/test_capture.py | 2 +- testing/test_session.py | 2 +- tox.ini | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f33663b2a..3c0d6aa40 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2401,7 +2401,7 @@ Bug fixes: teardown function are called earlier. - add an all-powerful metafunc.parametrize function which allows to parametrize test function arguments in multiple steps and therefore - from indepdenent plugins and palces. + from independent plugins and places. - add a @pytest.mark.parametrize helper which allows to easily call a test function with different argument values - Add examples to the "parametrize" example page, including a quick port diff --git a/_pytest/cacheprovider.py b/_pytest/cacheprovider.py index 0657001f2..893f0eae5 100755 --- a/_pytest/cacheprovider.py +++ b/_pytest/cacheprovider.py @@ -1,7 +1,7 @@ """ merged implementation of the cache provider -the name cache was not choosen to ensure pluggy automatically +the name cache was not chosen to ensure pluggy automatically ignores the external pytest-cache """ diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index b5f51eccf..f01235455 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -255,7 +255,7 @@ def pytest_assertrepr_compare(config, op, left, right): Return None for no custom explanation, otherwise return a list of strings. The strings will be joined by newlines but any newlines *in* a string will be escaped. Note that all but the first line will - be indented sligthly, the intention is for the first line to be a summary. + be indented slightly, the intention is for the first line to be a summary. """ # ------------------------------------------------------------------------- diff --git a/_pytest/main.py b/_pytest/main.py index a32352793..b66b661c8 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -81,7 +81,7 @@ def pytest_namespace(): def pytest_configure(config): - pytest.config = config # compatibiltiy + pytest.config = config # compatibility def wrap_session(config, doit): diff --git a/_pytest/mark.py b/_pytest/mark.py index 357a60492..d406bca6b 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -66,7 +66,7 @@ def pytest_collection_modifyitems(items, config): return # pytest used to allow "-" for negating # but today we just allow "-" at the beginning, use "not" instead - # we probably remove "-" alltogether soon + # we probably remove "-" altogether soon if keywordexpr.startswith("-"): keywordexpr = "not " + keywordexpr[1:] selectuntil = False diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 6c63bbbbf..25f7c5de7 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -566,7 +566,7 @@ class Testdir: def mkpydir(self, name): """Create a new python package. - This creates a (sub)direcotry with an empty ``__init__.py`` + This creates a (sub)directory with an empty ``__init__.py`` file so that is recognised as a python package. """ @@ -661,7 +661,7 @@ class Testdir: def inline_genitems(self, *args): """Run ``pytest.main(['--collectonly'])`` in-process. - Retuns a tuple of the collected items and a + Returns a tuple of the collected items and a :py:class:`HookRecorder` instance. This runs the :py:func:`pytest.main` function to run all of @@ -857,7 +857,7 @@ class Testdir: :py:meth:`parseconfigure`. :param withinit: Whether to also write a ``__init__.py`` file - to the temporarly directory to ensure it is a package. + to the temporary directory to ensure it is a package. """ kw = {self.request.function.__name__: Source(source).strip()} diff --git a/_pytest/python.py b/_pytest/python.py index 2973d43d6..3e865e9df 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -629,7 +629,7 @@ class Generator(FunctionMixin, PyCollector): def getcallargs(self, obj): if not isinstance(obj, (tuple, list)): obj = (obj,) - # explict naming + # explicit naming if isinstance(obj[0], py.builtin._basestring): name = obj[0] obj = obj[1:] diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 34eb9885b..276b9ba16 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -5,7 +5,7 @@ import sys import traceback import pytest -# for transfering markers +# for transferring markers import _pytest._code from _pytest.python import transfer_markers from _pytest.skipping import MarkEvaluator diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 9115d25e2..dfc9f60fb 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -364,7 +364,7 @@ class TestAssert_reprcompare: expl = '\n'.join(callequal(left, right, verbose=True)) assert expl.endswith(textwrap.dedent(expected).strip()) - def test_list_different_lenghts(self): + def test_list_different_lengths(self): expl = callequal([0, 1], [0, 1, 2]) assert len(expl) > 1 expl = callequal([0, 1, 2], [0, 1]) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index fdf674f25..7cc58e8a8 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -271,7 +271,7 @@ class TestAssertionRewrite: getmsg(f, must_pass=True) - def test_short_circut_evaluation(self): + def test_short_circuit_evaluation(self): def f(): assert True or explode # noqa diff --git a/testing/test_capture.py b/testing/test_capture.py index cbb5fc81b..763e28315 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -604,7 +604,7 @@ def test_capture_binary_output(testdir): def test_error_during_readouterr(testdir): - """Make sure we suspend capturing if errors occurr during readouterr""" + """Make sure we suspend capturing if errors occur during readouterr""" testdir.makepyfile(pytest_xyz=""" from _pytest.capture import FDCapture def bad_snap(self): diff --git a/testing/test_session.py b/testing/test_session.py index a7dcb27a4..f494dbc11 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -197,7 +197,7 @@ class TestNewSession(SessionTests): colfail = [x for x in finished if x.failed] assert len(colfail) == 1 - def test_minus_x_overriden_by_maxfail(self, testdir): + def test_minus_x_overridden_by_maxfail(self, testdir): testdir.makepyfile(__init__="") testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz") reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir) diff --git a/tox.ini b/tox.ini index e57fabd4b..1b9fb9f5a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion=2.0 distshare={homedir}/.tox/distshare -# make sure to update enviroment list on appveyor.yml +# make sure to update environment list on appveyor.yml envlist= linting py26 From 8f98ac5ae8774e51ac6336faa43d465fe013e747 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 15 Feb 2017 13:15:53 -0200 Subject: [PATCH 22/35] Fix typo in docs "textures" -> "fixtures" --- doc/en/writing_plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index de0aa1390..f6ed6e4e3 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -254,7 +254,7 @@ application modules: if ``myapp.testsupport.myplugin`` also declares ``pytest_plugins``, the contents of the variable will also be loaded as plugins, and so on. -This mechanism makes it easy to share textures within applications or even +This mechanism makes it easy to share fixtures within applications or even external applications without the need to create external plugins using the ``setuptools``'s entry point technique. From 58d7f4e0487bb4049c0472f1019a6117c6f25af4 Mon Sep 17 00:00:00 2001 From: Victor Uriarte Date: Thu, 16 Feb 2017 22:52:06 -0700 Subject: [PATCH 23/35] Correct typo --- _pytest/hookspec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index f01235455..ea1f0443d 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -246,7 +246,7 @@ def pytest_unconfigure(config): # ------------------------------------------------------------------------- -# hooks for customising the assert methods +# hooks for customizing the assert methods # ------------------------------------------------------------------------- def pytest_assertrepr_compare(config, op, left, right): From a88017cf262b7d1b8d62d756232db1b5aa81542c Mon Sep 17 00:00:00 2001 From: Victor Uriarte Date: Thu, 16 Feb 2017 22:53:51 -0700 Subject: [PATCH 24/35] Add note documenting #2257 --- _pytest/hookspec.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index ea1f0443d..552a06575 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -263,7 +263,14 @@ def pytest_assertrepr_compare(config, op, left, right): # ------------------------------------------------------------------------- def pytest_report_header(config, startdir): - """ return a string to be displayed as header info for terminal reporting.""" + """ return a string to be displayed as header info for terminal reporting. + + .. note:: + + This function should be implemented only in plugins or ``conftest.py`` + files situated at the tests root directory due to how pytest + :ref:`discovers plugins during startup `. + """ @hookspec(firstresult=True) def pytest_report_teststatus(report): From 5fd010c4c3f53d69bcef396989e52a6cbfa59c4e Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 19 Feb 2017 09:02:35 -0800 Subject: [PATCH 25/35] Simplify travis.yml with tox environment variables --- .travis.yml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index f701302d9..1dedddfb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,34 +8,34 @@ install: "pip install -U tox" env: matrix: # coveralls is not listed in tox's envlist, but should run in travis - - TESTENV=coveralls + - TOXENV=coveralls # note: please use "tox --listenvs" to populate the build matrix below - - TESTENV=linting - - TESTENV=py26 - - TESTENV=py27 - - TESTENV=py33 - - TESTENV=py34 - - TESTENV=py35 - - TESTENV=pypy - - TESTENV=py27-pexpect - - TESTENV=py27-xdist - - TESTENV=py27-trial - - TESTENV=py35-pexpect - - TESTENV=py35-xdist - - TESTENV=py35-trial - - TESTENV=py27-nobyte - - TESTENV=doctesting - - TESTENV=freeze - - TESTENV=docs + - TOXENV=linting + - TOXENV=py26 + - TOXENV=py27 + - TOXENV=py33 + - TOXENV=py34 + - TOXENV=py35 + - TOXENV=pypy + - TOXENV=py27-pexpect + - TOXENV=py27-xdist + - TOXENV=py27-trial + - TOXENV=py35-pexpect + - TOXENV=py35-xdist + - TOXENV=py35-trial + - TOXENV=py27-nobyte + - TOXENV=doctesting + - TOXENV=freeze + - TOXENV=docs matrix: include: - - env: TESTENV=py36 + - env: TOXENV=py36 python: '3.6-dev' - - env: TESTENV=py37 + - env: TOXENV=py37 python: 'nightly' -script: tox --recreate -e $TESTENV +script: tox --recreate notifications: irc: From d3a6be413014b112227cbdb489c62483ec4d9192 Mon Sep 17 00:00:00 2001 From: Katerina Koukiou Date: Wed, 22 Feb 2017 14:17:45 +0100 Subject: [PATCH 26/35] junitxml: Fix double system-out tags per testcase In the xml report we now have two occurences for the system-out tag if the testcase writes to stdout both on call and teardown and fails in teardown. This behaviour is against the xsd. This patch makes sure that the system-out section exists only once per testcase. --- CHANGELOG.rst | 4 ++++ _pytest/junitxml.py | 9 ++++----- testing/test_junitxml.py | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a07062437..d309125d9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,9 @@ 3.0.7 (unreleased) ================== +* junitxml: Fix problematic case where system-out tag occured twice per testcase + element in the XML report. Thanks `@kkoukiou`_ for the PR. + * Fix regression, pytest now skips unittest correctly if run with ``--pdb`` (`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR. @@ -31,6 +34,7 @@ .. _@gst: https://github.com/gst .. _@sirex: https://github.com/sirex .. _@vidartf: https://github.com/vidartf +.. _@kkoukiou: https://github.com/KKoukiou .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 .. _#2160: https://github.com/pytest-dev/pytest/issues/2160 diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 317382e63..3f371c9d3 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -119,7 +119,7 @@ class _NodeReporter(object): node = kind(data, message=message) self.append(node) - def _write_captured_output(self, report): + def write_captured_output(self, report): for capname in ('out', 'err'): content = getattr(report, 'capstd' + capname) if content: @@ -128,7 +128,6 @@ class _NodeReporter(object): def append_pass(self, report): self.add_stats('passed') - self._write_captured_output(report) def append_failure(self, report): # msg = str(report.longrepr.reprtraceback.extraline) @@ -147,7 +146,6 @@ class _NodeReporter(object): fail = Junit.failure(message=message) fail.append(bin_xml_escape(report.longrepr)) self.append(fail) - self._write_captured_output(report) def append_collect_error(self, report): # msg = str(report.longrepr.reprtraceback.extraline) @@ -165,7 +163,6 @@ class _NodeReporter(object): msg = "test setup failure" self._add_simple( Junit.error, msg, report.longrepr) - self._write_captured_output(report) def append_skipped(self, report): if hasattr(report, "wasxfail"): @@ -180,7 +177,7 @@ class _NodeReporter(object): Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason), type="pytest.skip", message=skipreason)) - self._write_captured_output(report) + self.write_captured_output(report) def finalize(self): data = self.to_xml().unicode(indent=0) @@ -345,6 +342,8 @@ class LogXML(object): reporter.append_skipped(report) self.update_testcase_duration(report) if report.when == "teardown": + reporter = self._opentestcase(report) + reporter.write_captured_output(report) self.finalize(report) def update_testcase_duration(self, report): diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index abbc9cd33..d167f735d 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -557,6 +557,25 @@ class TestPython: systemout = pnode.find_first_by_tag("system-err") assert "hello-stderr" in systemout.toxml() + def test_avoid_double_stdout(self, testdir): + testdir.makepyfile(""" + import sys + import pytest + + @pytest.fixture + def arg(request): + yield + sys.stdout.write('hello-stdout teardown') + raise ValueError() + def test_function(arg): + sys.stdout.write('hello-stdout call') + """) + result, dom = runandparse(testdir) + node = dom.find_first_by_tag("testsuite") + pnode = node.find_first_by_tag("testcase") + systemout = pnode.find_first_by_tag("system-out") + assert "hello-stdout call" in systemout.toxml() + assert "hello-stdout teardown" in systemout.toxml() def test_mangle_test_address(): from _pytest.junitxml import mangle_test_address From bb5f200ed7bd75934235e53bc3acfac9c9251c7e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 25 Feb 2017 12:06:51 -0300 Subject: [PATCH 27/35] Improve docs for yield-fixture and with statement a bit Fix #2262 --- doc/en/fixture.rst | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 0a9000611..8547a89a6 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -243,7 +243,9 @@ Fixture finalization / executing teardown code pytest supports execution of fixture specific finalization code when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all -the code after the *yield* statement serves as the teardown code.:: +the code after the *yield* statement serves as the teardown code: + +.. code-block:: python # content of conftest.py @@ -275,22 +277,23 @@ occur around each single test. In either case the test module itself does not need to change or know about these details of fixture setup. -Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements:: +Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements: + +.. code-block:: python # content of test_yield2.py + import smtplib import pytest - @pytest.fixture - def passwd(): - with open("/etc/passwd") as f: - yield f.readlines() + @pytest.fixture(scope="module") + def smtp(request): + with smtplib.SMTP("smtp.gmail.com") as smtp: + yield smtp # provide the fixture value - def test_has_lines(passwd): - assert len(passwd) >= 1 -The file ``f`` will be closed after the test finished execution -because the Python ``file`` object supports finalization when +The ``smtp`` connection will be closed after the test finished execution +because the ``smtp`` object automatically closes when the ``with`` statement ends. From 6a52fe165070dcbeda9b7c5b622975effcab5f40 Mon Sep 17 00:00:00 2001 From: Omer Hadari Date: Sat, 4 Mar 2017 10:26:46 +0200 Subject: [PATCH 28/35] fixed internal error on unprintable raised AssertionErrors --- _pytest/_code/code.py | 6 ++++-- testing/test_assertion.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 616d5c431..6eceb0c7f 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -352,6 +352,8 @@ class ExceptionInfo(object): help for navigating the traceback. """ _striptext = '' + _assert_start_repr = "AssertionError(u\'assert " if sys.version_info[0] < 3 else "AssertionError(\'assert " + def __init__(self, tup=None, exprinfo=None): import _pytest._code if tup is None: @@ -359,8 +361,8 @@ class ExceptionInfo(object): if exprinfo is None and isinstance(tup[1], AssertionError): exprinfo = getattr(tup[1], 'msg', None) if exprinfo is None: - exprinfo = py._builtin._totext(tup[1]) - if exprinfo and exprinfo.startswith('assert '): + exprinfo = py.io.saferepr(tup[1]) + if exprinfo and exprinfo.startswith(self._assert_start_repr): self._striptext = 'AssertionError: ' self._excinfo = tup #: the exception class diff --git a/testing/test_assertion.py b/testing/test_assertion.py index dfc9f60fb..bc814590a 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -916,6 +916,25 @@ def test_assert_with_unicode(monkeypatch, testdir): result = testdir.runpytest() result.stdout.fnmatch_lines(['*AssertionError*']) +def test_raise_unprintable_assertion_error(testdir): + testdir.makepyfile(r""" + def test_raise_assertion_error(): + raise AssertionError('\xff') + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines([r"> raise AssertionError('\xff')", 'E AssertionError: *']) + +def test_raise_assertion_error_raisin_repr(testdir): + testdir.makepyfile(u""" + class RaisingRepr(object): + def __repr__(self): + raise Exception() + def test_raising_repr(): + raise AssertionError(RaisingRepr()) + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(['E AssertionError: ']) + def test_issue_1944(testdir): testdir.makepyfile(""" def f(): From 6aaf7ae18b2c786bbf7ff07d223860ee3f98c20c Mon Sep 17 00:00:00 2001 From: Omer Hadari Date: Sat, 4 Mar 2017 10:32:14 +0200 Subject: [PATCH 29/35] added to authors and changelog --- AUTHORS | 1 + CHANGELOG.rst | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index b49405bf7..c55be41e3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -147,3 +147,4 @@ Victor Uriarte Vidar T. Fauske Wouter van Ackooy Xuecong Liao +Omer Hadari diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 32726274f..de3f334c2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,7 +22,8 @@ * Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_). Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR. -* +* Fix internal errors when an unprintable ``AssertionError`` is raised inside a test. + Thanks `@omerhadari`_ for the PR. * Skipping plugin now also works with test items generated by custom collectors (`#2231`_). Thanks to `@vidartf`_. From f71467f5b1c84fd662d4f1cca582e2d2d34a08d7 Mon Sep 17 00:00:00 2001 From: Omer Hadari Date: Sat, 4 Mar 2017 10:55:59 +0200 Subject: [PATCH 30/35] added link to changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index de3f334c2..216891484 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -43,6 +43,7 @@ .. _@sirex: https://github.com/sirex .. _@vidartf: https://github.com/vidartf .. _@kkoukiou: https://github.com/KKoukiou +.. _@omerhadari: https://github.com/omerhadari .. _#2248: https://github.com/pytest-dev/pytest/issues/2248 .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 From b61dcded3781bdcd4c76ccb614e1df58a622e982 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 4 Mar 2017 07:17:39 -0300 Subject: [PATCH 31/35] Allow py37-nightly to fail on Travis Related to #2285 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1dedddfb4..41537a466 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,9 @@ matrix: python: '3.6-dev' - env: TOXENV=py37 python: 'nightly' + allow_failures: + - env: TOXENV=py37 + python: 'nightly' script: tox --recreate From 02dc54531150d715725885cb678209a4048142fb Mon Sep 17 00:00:00 2001 From: Omer Hadari Date: Sat, 4 Mar 2017 12:49:00 +0200 Subject: [PATCH 32/35] added in the correct alphabitcal order --- AUTHORS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index c55be41e3..2332a51ef 100644 --- a/AUTHORS +++ b/AUTHORS @@ -101,7 +101,6 @@ Mathieu Clabaut Matt Bachmann Matt Williams Matthias Hafner -mbyt Michael Aquilina Michael Birtwell Michael Droettboom @@ -113,6 +112,7 @@ Nicolas Delaby Oleg Pidsadnyi Oliver Bestwalter Omar Kohl +Omer Hadari Patrick Hayes Pieter Mulder Piotr Banaszkiewicz @@ -147,4 +147,4 @@ Victor Uriarte Vidar T. Fauske Wouter van Ackooy Xuecong Liao -Omer Hadari +mbyt From dd25ae7f3369fdbb4ec9fe3d50f0e4b25c9e6c25 Mon Sep 17 00:00:00 2001 From: Omer Hadari Date: Sat, 4 Mar 2017 12:50:02 +0200 Subject: [PATCH 33/35] added in the correct alphabitcal order --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 2332a51ef..821bd2a6d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -101,6 +101,7 @@ Mathieu Clabaut Matt Bachmann Matt Williams Matthias Hafner +mbyt Michael Aquilina Michael Birtwell Michael Droettboom @@ -147,4 +148,3 @@ Victor Uriarte Vidar T. Fauske Wouter van Ackooy Xuecong Liao -mbyt From e05ff0338a0220b63de93461eacf0706d55f8623 Mon Sep 17 00:00:00 2001 From: "NODA, Kai" Date: Mon, 6 Mar 2017 01:01:55 +0800 Subject: [PATCH 34/35] assert.rst: typographical correction --- doc/en/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index d93da178d..aeb9a5d35 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -287,5 +287,5 @@ For further information, Benjamin Peterson wrote up `Behind the scenes of pytest ``--nomagic``. .. versionchanged:: 3.0 - Removes the ``--no-assert`` and``--nomagic`` options. + Removes the ``--no-assert`` and ``--nomagic`` options. Removes the ``--assert=reinterp`` option. From cee578e32786986cb79495a2ea0e5c5e4eb425ce Mon Sep 17 00:00:00 2001 From: fbjorn Date: Sun, 5 Mar 2017 22:44:13 +0300 Subject: [PATCH 35/35] Fix trailing whitespace in terminal output --- AUTHORS | 1 + CHANGELOG.rst | 6 ++++-- _pytest/terminal.py | 4 ++-- testing/test_config.py | 2 +- testing/test_terminal.py | 9 +++++++++ 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 821bd2a6d..ce19f1f21 100644 --- a/AUTHORS +++ b/AUTHORS @@ -43,6 +43,7 @@ Dave Hunt David Díaz-Barquero David Mohr David Vierra +Denis Kirisov Diego Russo Dmitry Dygalo Duncan Betts diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 216891484..cf3df9c87 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,8 +27,8 @@ * Skipping plugin now also works with test items generated by custom collectors (`#2231`_). Thanks to `@vidartf`_. - -* + +* Fix trailing whitespace in console output if no .ini file presented (`#2281`_). Thanks `@fbjorn`_ for the PR. * Conditionless ``xfail`` markers no longer rely on the underlying test item being an instance of ``PyobjMixin``, and can therefore apply to tests not @@ -44,6 +44,7 @@ .. _@vidartf: https://github.com/vidartf .. _@kkoukiou: https://github.com/KKoukiou .. _@omerhadari: https://github.com/omerhadari +.. _@fbjorn: https://github.com/fbjorn .. _#2248: https://github.com/pytest-dev/pytest/issues/2248 .. _#2137: https://github.com/pytest-dev/pytest/issues/2137 @@ -51,6 +52,7 @@ .. _#2231: https://github.com/pytest-dev/pytest/issues/2231 .. _#2234: https://github.com/pytest-dev/pytest/issues/2234 .. _#2238: https://github.com/pytest-dev/pytest/issues/2238 +.. _#2281: https://github.com/pytest-dev/pytest/issues/2281 .. _PEP-479: https://www.python.org/dev/peps/pep-0479/ diff --git a/_pytest/terminal.py b/_pytest/terminal.py index 16bf75733..79e065329 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -295,8 +295,8 @@ class TerminalReporter: def pytest_report_header(self, config): inifile = "" if config.inifile: - inifile = config.rootdir.bestrelpath(config.inifile) - lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)] + inifile = " " + config.rootdir.bestrelpath(config.inifile) + lines = ["rootdir: %s, inifile:%s" % (config.rootdir, inifile)] plugininfo = config.pluginmanager.list_plugin_distinfo() if plugininfo: diff --git a/testing/test_config.py b/testing/test_config.py index 3ce51d639..b6ccd7085 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -519,7 +519,7 @@ def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args): args[i] = d2 with root.as_cwd(): result = testdir.runpytest(*args) - result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: ']) + result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile:']) @pytest.mark.skipif("sys.platform == 'win32'") diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 3efd7b1f9..0f919b5ed 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -906,3 +906,12 @@ def test_summary_stats(exp_line, exp_color, stats_arg): print("Actually got: \"%s\"; with color \"%s\"" % (line, color)) assert line == exp_line assert color == exp_color + + +def test_no_trailing_whitespace_after_inifile_word(testdir): + result = testdir.runpytest('') + assert 'inifile:\n' in result.stdout.str() + + testdir.makeini('[pytest]') + result = testdir.runpytest('') + assert 'inifile: tox.ini\n' in result.stdout.str()