diff --git a/AUTHORS b/AUTHORS index c35e6658739..462dc71e673 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,6 +8,7 @@ Alexei Kozlenok Anatoly Bubenkoff Andreas Zeidler Andy Freeland +Andrzej Ostrowski Anthon van der Neut Armin Rigo Aron Curzon diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0ec3200eb6f..b3eb6e7e1bf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,6 +27,23 @@ Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and `@tomviner`_ for PR. +* Rename ``getfuncargvalue`` to ``getfixturevalue``. ``getfuncargvalue`` is + deprecated but still present. Thanks to `@RedBeardCode`_ and `@tomviner`_ + for PR (`#1626`_). + +* Always include full assertion explanation. The previous behaviour was hiding + sub-expressions that happened to be False, assuming this was redundant information. + Thanks `@bagerard`_ for reporting (`#1503`_). Thanks to `@davehunt`_ and + `@tomviner`_ for PR. + +* Now pytest warnings summary is shown up by default. Added a new flag + ``--disable-pytest-warnings`` to explicitly disable the warnings summary. + This change resolves the (`#1668`_). + +* Renamed the pytest ``pdb`` module (plugin) into ``debugging``. + +* + * .. _#1580: https://github.com/pytest-dev/pytest/pull/1580 @@ -34,12 +51,17 @@ .. _#1597: https://github.com/pytest-dev/pytest/pull/1597 .. _#460: https://github.com/pytest-dev/pytest/pull/460 .. _#1553: https://github.com/pytest-dev/pytest/issues/1553 +.. _#1626: https://github.com/pytest-dev/pytest/pull/1626 +.. _#1503: https://github.com/pytest-dev/pytest/issues/1503 +.. _#1668: https://github.com/pytest-dev/pytest/issues/1668 .. _@graingert: https://github.com/graingert .. _@taschini: https://github.com/taschini .. _@nikratio: https://github.com/nikratio .. _@RedBeardCode: https://github.com/RedBeardCode .. _@Vogtinator: https://github.com/Vogtinator +.. _@bagerard: https://github.com/bagerard +.. _@davehunt: https://github.com/davehunt 2.9.2 diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py index 8bf425caf48..2481cf34ccd 100644 --- a/_pytest/assertion/util.py +++ b/_pytest/assertion/util.py @@ -38,44 +38,11 @@ def format_explanation(explanation): displaying diffs. """ explanation = ecu(explanation) - explanation = _collapse_false(explanation) lines = _split_explanation(explanation) result = _format_lines(lines) return u('\n').join(result) -def _collapse_false(explanation): - """Collapse expansions of False - - So this strips out any "assert False\n{where False = ...\n}" - blocks. - """ - where = 0 - while True: - start = where = explanation.find("False\n{False = ", where) - if where == -1: - break - level = 0 - prev_c = explanation[start] - for i, c in enumerate(explanation[start:]): - if prev_c + c == "\n{": - level += 1 - elif prev_c + c == "\n}": - level -= 1 - if not level: - break - prev_c = c - else: - raise AssertionError("unbalanced braces: %r" % (explanation,)) - end = start + i - where = end - if explanation[end - 1] == '\n': - explanation = (explanation[:start] + explanation[start+15:end-1] + - explanation[end+1:]) - where -= 17 - return explanation - - def _split_explanation(explanation): """Return a list of individual lines in the explanation diff --git a/_pytest/config.py b/_pytest/config.py index 9a308df2bb9..caadacd69c9 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -63,7 +63,7 @@ class UsageError(Exception): _preinit = [] default_plugins = ( - "mark main terminal runner python pdb unittest capture skipping " + "mark main terminal runner python debugging unittest capture skipping " "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript " "junitxml resultlog doctest cacheprovider").split() diff --git a/_pytest/pdb.py b/_pytest/debugging.py similarity index 100% rename from _pytest/pdb.py rename to _pytest/debugging.py diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 72b94f488c6..1fd8b419be9 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -70,7 +70,7 @@ def __init__(self, name, parent, runner=None, dtest=None): def setup(self): if self.dtest is not None: self.fixture_request = _setup_fixtures(self) - globs = dict(getfixture=self.fixture_request.getfuncargvalue) + globs = dict(getfixture=self.fixture_request.getfixturevalue) self.dtest.globs.update(globs) def runtest(self): diff --git a/_pytest/python.py b/_pytest/python.py index 92fc8aa1fc3..502fdc7ef7b 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -5,6 +5,7 @@ import re import types import sys +import warnings import py import pytest @@ -1469,7 +1470,7 @@ def _getnextfixturedef(self, argname): fixturedefs = self._arg2fixturedefs.get(argname, None) if fixturedefs is None: # we arrive here because of a a dynamic call to - # getfuncargvalue(argname) usage which was naturally + # getfixturevalue(argname) usage which was naturally # not known at parsing/collection time fixturedefs = self._fixturemanager.getfixturedefs( argname, self._pyfuncitem.parent.nodeid) @@ -1564,7 +1565,7 @@ def _fillfixtures(self): fixturenames = getattr(item, "fixturenames", self.fixturenames) for argname in fixturenames: if argname not in item.funcargs: - item.funcargs[argname] = self.getfuncargvalue(argname) + item.funcargs[argname] = self.getfixturevalue(argname) def cached_setup(self, setup, teardown=None, scope="module", extrakey=None): """ (deprecated) Return a testing resource managed by ``setup`` & @@ -1598,17 +1599,23 @@ def finalizer(): self._addfinalizer(finalizer, scope=scope) return val - def getfuncargvalue(self, argname): - """ Dynamically retrieve a named fixture function argument. + def getfixturevalue(self, argname): + """ Dynamically run a named fixture function. - As of pytest-2.3, it is easier and usually better to access other - fixture values by stating it as an input argument in the fixture - function. If you only can decide about using another fixture at test + Declaring fixtures via function argument is recommended where possible. + But if you can only decide whether to use another fixture at test setup time, you may use this function to retrieve it inside a fixture - function body. + or test function body. """ return self._get_active_fixturedef(argname).cached_result[0] + def getfuncargvalue(self, argname): + """ Deprecated, use getfixturevalue. """ + warnings.warn( + "use of getfuncargvalue is deprecated, use getfixturevalue", + DeprecationWarning) + return self.getfixturevalue(argname) + def _get_active_fixturedef(self, argname): try: return self._fixturedefs[argname] @@ -1624,7 +1631,7 @@ class PseudoFixtureDef: raise # remove indent to prevent the python3 exception # from leaking into the call - result = self._getfuncargvalue(fixturedef) + result = self._getfixturevalue(fixturedef) self._funcargs[argname] = result self._fixturedefs[argname] = fixturedef return fixturedef @@ -1640,7 +1647,7 @@ def _get_fixturestack(self): l.append(fixturedef) current = current._parent_request - def _getfuncargvalue(self, fixturedef): + def _getfixturevalue(self, fixturedef): # prepare a subrequest object before calling fixture function # (latter managed by fixturedef) argname = fixturedef.argname diff --git a/_pytest/terminal.py b/_pytest/terminal.py index 825f553ef2c..ed58c4d2f24 100644 --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -20,10 +20,15 @@ def pytest_addoption(parser): group._addoption('-q', '--quiet', action="count", dest="quiet", default=0, help="decrease verbosity."), group._addoption('-r', - action="store", dest="reportchars", default=None, metavar="chars", + action="store", dest="reportchars", default='', metavar="chars", help="show extra test summary info as specified by chars (f)ailed, " - "(E)error, (s)skipped, (x)failed, (X)passed (w)pytest-warnings " - "(p)passed, (P)passed with output, (a)all except pP.") + "(E)error, (s)skipped, (x)failed, (X)passed, " + "(p)passed, (P)passed with output, (a)all except pP. " + "The pytest warnings are displayed at all times except when " + "--disable-pytest-warnings is set") + group._addoption('--disable-pytest-warnings', default=False, + dest='disablepytestwarnings', action='store_true', + help='disable warnings summary, overrides -r w flag') group._addoption('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default).") @@ -66,6 +71,10 @@ def getreportopt(config): elif setting == "xfailed": reportopts += "x" reportchars = config.option.reportchars + if not config.option.disablepytestwarnings and 'w' not in reportchars: + reportchars += 'w' + elif config.option.disablepytestwarnings and 'w' in reportchars: + reportchars = reportchars.replace('w', '') if reportchars: for char in reportchars: if char not in reportopts and char != 'a': diff --git a/doc/en/genapi.py b/doc/en/genapi.py index f8cdda6cfbf..89ddc873169 100644 --- a/doc/en/genapi.py +++ b/doc/en/genapi.py @@ -32,7 +32,7 @@ def docmethod(self, method): def pytest_funcarg__a(request): with Writer("request") as writer: - writer.docmethod(request.getfuncargvalue) + writer.docmethod(request.getfixturevalue) writer.docmethod(request.cached_setup) writer.docmethod(request.addfinalizer) writer.docmethod(request.applymarker) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index ba047f2a9f8..46d83363eda 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -327,7 +327,7 @@ class TestFormattedExcinfo: def pytest_funcarg__importasmod(self, request): def importasmod(source): source = _pytest._code.Source(source) - tmpdir = request.getfuncargvalue("tmpdir") + tmpdir = request.getfixturevalue("tmpdir") modpath = tmpdir.join("mod.py") tmpdir.ensure("__init__.py") modpath.write(source) diff --git a/testing/cx_freeze/runtests_setup.py b/testing/cx_freeze/runtests_setup.py index a2874a655eb..01e8a8a8929 100644 --- a/testing/cx_freeze/runtests_setup.py +++ b/testing/cx_freeze/runtests_setup.py @@ -8,7 +8,7 @@ setup( name="runtests", version="0.1", - description="exemple of how embedding py.test into an executable using cx_freeze", + description="example of how embedding py.test into an executable using cx_freeze", executables=[Executable("runtests_script.py")], options={"build_exe": {'includes': pytest.freeze_includes()}}, ) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 85de0c3189a..435f7947cd7 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -93,12 +93,12 @@ def test_conftest_funcargs_only_available_in_subdir(self, testdir): sub1.join("conftest.py").write(_pytest._code.Source(""" import pytest def pytest_funcarg__arg1(request): - pytest.raises(Exception, "request.getfuncargvalue('arg2')") + pytest.raises(Exception, "request.getfixturevalue('arg2')") """)) sub2.join("conftest.py").write(_pytest._code.Source(""" import pytest def pytest_funcarg__arg2(request): - pytest.raises(Exception, "request.getfuncargvalue('arg1')") + pytest.raises(Exception, "request.getfixturevalue('arg1')") """)) sub1.join("test_in_sub1.py").write("def test_1(arg1): pass") @@ -435,21 +435,23 @@ def test_method(self, something): assert len(arg2fixturedefs) == 1 assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something" - def test_getfuncargvalue_recursive(self, testdir): + def test_getfixturevalue_recursive(self, testdir): testdir.makeconftest(""" def pytest_funcarg__something(request): return 1 """) testdir.makepyfile(""" def pytest_funcarg__something(request): - return request.getfuncargvalue("something") + 1 + return request.getfixturevalue("something") + 1 def test_func(something): assert something == 2 """) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) - def test_getfuncargvalue(self, testdir): + @pytest.mark.parametrize( + 'getfixmethod', ('getfixturevalue', 'getfuncargvalue')) + def test_getfixturevalue(self, testdir, getfixmethod): item = testdir.getitem(""" l = [2] def pytest_funcarg__something(request): return 1 @@ -458,14 +460,15 @@ def pytest_funcarg__other(request): def test_func(something): pass """) req = item._request - pytest.raises(FixtureLookupError, req.getfuncargvalue, "notexists") - val = req.getfuncargvalue("something") + fixture_fetcher = getattr(req, getfixmethod) + pytest.raises(FixtureLookupError, fixture_fetcher, "notexists") + val = fixture_fetcher("something") assert val == 1 - val = req.getfuncargvalue("something") + val = fixture_fetcher("something") assert val == 1 - val2 = req.getfuncargvalue("other") + val2 = fixture_fetcher("other") assert val2 == 2 - val2 = req.getfuncargvalue("other") # see about caching + val2 = fixture_fetcher("other") # see about caching assert val2 == 2 pytest._fillfuncargs(item) assert item.funcargs["something"] == 1 @@ -812,10 +815,10 @@ def test_two_different_setups(arg1, arg2): "*1 passed*" ]) - def test_request_cached_setup_getfuncargvalue(self, testdir): + def test_request_cached_setup_getfixturevalue(self, testdir): testdir.makepyfile(""" def pytest_funcarg__arg1(request): - arg1 = request.getfuncargvalue("arg2") + arg1 = request.getfixturevalue("arg2") return request.cached_setup(lambda: arg1 + 1) def pytest_funcarg__arg2(request): return request.cached_setup(lambda: 10) @@ -1118,7 +1121,7 @@ def test_2(arg2): class TestFixtureManagerParseFactories: def pytest_funcarg__testdir(self, request): - testdir = request.getfuncargvalue("testdir") + testdir = request.getfixturevalue("testdir") testdir.makeconftest(""" def pytest_funcarg__hello(request): return "conftest" @@ -1804,9 +1807,9 @@ def test_4(arg, created, finalized): reprec.assertoutcome(passed=4) @pytest.mark.parametrize("method", [ - 'request.getfuncargvalue("arg")', + 'request.getfixturevalue("arg")', 'request.cached_setup(lambda: None, scope="function")', - ], ids=["getfuncargvalue", "cached_setup"]) + ], ids=["getfixturevalue", "cached_setup"]) def test_scope_mismatch_various(self, testdir, method): testdir.makeconftest(""" import pytest @@ -2737,6 +2740,7 @@ def test_1(arg1): *def arg1* """) + class TestParameterizedSubRequest: def test_call_from_fixture(self, testdir): testfile = testdir.makepyfile(""" @@ -2748,7 +2752,7 @@ def fix_with_param(request): @pytest.fixture def get_named_fixture(request): - return request.getfuncargvalue('fix_with_param') + return request.getfixturevalue('fix_with_param') def test_foo(request, get_named_fixture): pass @@ -2773,7 +2777,7 @@ def fix_with_param(request): return request.param def test_foo(request): - request.getfuncargvalue('fix_with_param') + request.getfixturevalue('fix_with_param') """) result = testdir.runpytest() result.stdout.fnmatch_lines(""" @@ -2797,7 +2801,7 @@ def fix_with_param(request): testfile = testdir.makepyfile(""" def test_foo(request): - request.getfuncargvalue('fix_with_param') + request.getfixturevalue('fix_with_param') """) result = testdir.runpytest() result.stdout.fnmatch_lines(""" @@ -2827,7 +2831,7 @@ def fix_with_param(request): from fix import fix_with_param def test_foo(request): - request.getfuncargvalue('fix_with_param') + request.getfixturevalue('fix_with_param') """)) tests_dir.chdir() @@ -2842,3 +2846,7 @@ def test_foo(request): E*{1}:5 *1 failed* """.format(fixfile.strpath, testfile.basename)) + + +def test_getfuncargvalue_is_deprecated(request): + pytest.deprecated_call(request.getfuncargvalue, 'tmpdir') diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 91a157673b7..13c70957943 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -720,14 +720,14 @@ def test_hello(arg1, arg2): "*4 failed*", ]) - def test_parametrize_and_inner_getfuncargvalue(self, testdir): + def test_parametrize_and_inner_getfixturevalue(self, testdir): p = testdir.makepyfile(""" def pytest_generate_tests(metafunc): metafunc.parametrize("arg1", [1], indirect=True) metafunc.parametrize("arg2", [10], indirect=True) def pytest_funcarg__arg1(request): - x = request.getfuncargvalue("arg2") + x = request.getfixturevalue("arg2") return x + request.param def pytest_funcarg__arg2(request): diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index f43c424ca94..a47d8a75f96 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -213,10 +213,12 @@ def x(): return False def f(): assert x() and x() - assert getmsg(f, {"x" : x}) == "assert (x())" + assert getmsg(f, {"x" : x}) == """assert (False) + + where False = x()""" def f(): assert False or x() - assert getmsg(f, {"x" : x}) == "assert (False or x())" + assert getmsg(f, {"x" : x}) == """assert (False or False) + + where False = x()""" def f(): assert 1 in {} and 2 in {} assert getmsg(f) == "assert (1 in {})" @@ -299,27 +301,34 @@ def g(a=42, *args, **kwargs): ns = {"g" : g} def f(): assert g() - assert getmsg(f, ns) == """assert g()""" + assert getmsg(f, ns) == """assert False + + where False = g()""" def f(): assert g(1) - assert getmsg(f, ns) == """assert g(1)""" + assert getmsg(f, ns) == """assert False + + where False = g(1)""" def f(): assert g(1, 2) - assert getmsg(f, ns) == """assert g(1, 2)""" + assert getmsg(f, ns) == """assert False + + where False = g(1, 2)""" def f(): assert g(1, g=42) - assert getmsg(f, ns) == """assert g(1, g=42)""" + assert getmsg(f, ns) == """assert False + + where False = g(1, g=42)""" def f(): assert g(1, 3, g=23) - assert getmsg(f, ns) == """assert g(1, 3, g=23)""" + assert getmsg(f, ns) == """assert False + + where False = g(1, 3, g=23)""" def f(): seq = [1, 2, 3] assert g(*seq) - assert getmsg(f, ns) == """assert g(*[1, 2, 3])""" + assert getmsg(f, ns) == """assert False + + where False = g(*[1, 2, 3])""" def f(): x = "a" assert g(**{x : 2}) - assert getmsg(f, ns) == """assert g(**{'a': 2})""" + assert getmsg(f, ns) == """assert False + + where False = g(**{'a': 2})""" def test_attribute(self): class X(object): @@ -332,7 +341,8 @@ def f(): def f(): x.a = False # noqa assert x.a # noqa - assert getmsg(f, ns) == """assert x.a""" + assert getmsg(f, ns) == """assert False + + where False = x.a""" def test_comparisons(self): def f(): @@ -710,7 +720,3 @@ def test_long_repr(): """) result = testdir.runpytest() assert 'unbalanced braces' not in result.stdout.str() - - -def test_collapse_false_unbalanced_braces(): - util._collapse_false('some text{ False\n{False = some more text\n}') diff --git a/testing/test_config.py b/testing/test_config.py index fe06540173e..f6511b8d48c 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -517,11 +517,11 @@ def fix(request): def test_hello(fix): pass """) - result = testdir.runpytest() + result = testdir.runpytest("--disable-pytest-warnings") assert result.parseoutcomes()["pytest-warnings"] > 0 assert "hello" not in result.stdout.str() - result = testdir.runpytest("-rw") + result = testdir.runpytest() result.stdout.fnmatch_lines(""" ===*pytest-warning summary*=== *WT1*test_warn_on_test_item*:5*hello* diff --git a/testing/test_genscript.py b/testing/test_genscript.py index 1260a5a6b26..1d51c6e8d78 100644 --- a/testing/test_genscript.py +++ b/testing/test_genscript.py @@ -8,7 +8,7 @@ def standalone(request): class Standalone: def __init__(self, request): - self.testdir = request.getfuncargvalue("testdir") + self.testdir = request.getfixturevalue("testdir") script = "mypytest" result = self.testdir.runpytest("--genscript=%s" % script) assert result.ret == 0 diff --git a/testing/test_pdb.py b/testing/test_pdb.py index eeddcf0ae80..44163a2040f 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -13,11 +13,11 @@ def runpdb_and_get_report(testdir, source): class TestPDB: def pytest_funcarg__pdblist(self, request): - monkeypatch = request.getfuncargvalue("monkeypatch") + monkeypatch = request.getfixturevalue("monkeypatch") pdblist = [] def mypdb(*args): pdblist.append(args) - plugin = request.config.pluginmanager.getplugin('pdb') + plugin = request.config.pluginmanager.getplugin('debugging') monkeypatch.setattr(plugin, 'post_mortem', mypdb) return pdblist diff --git a/testing/test_terminal.py b/testing/test_terminal.py index be5749c96d2..96ed80e08c6 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -591,6 +591,7 @@ def test_getreportopt(): class config: class option: reportchars = "" + disablepytestwarnings = True config.option.report = "xfailed" assert getreportopt(config) == "x" @@ -601,12 +602,21 @@ class option: assert getreportopt(config) == "sx" config.option.report = "skipped" - config.option.reportchars = "sf" + config.option.reportchars = "sfw" assert getreportopt(config) == "sf" - config.option.reportchars = "sfx" + config.option.reportchars = "sfxw" assert getreportopt(config) == "sfx" + config.option.reportchars = "sfx" + config.option.disablepytestwarnings = False + assert getreportopt(config) == "sfxw" + + config.option.reportchars = "sfxw" + config.option.disablepytestwarnings = False + assert getreportopt(config) == "sfxw" + + def test_terminalreporter_reportopt_addopts(testdir): testdir.makeini("[pytest]\naddopts=-rs") testdir.makepyfile("""