From 0385c273439d105e3cc5131759cd44e66702b5ee Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 14 Nov 2018 21:55:37 +0100 Subject: [PATCH 01/18] cacheprovider: do not write README/.gitignore to existing dir Fixes https://github.com/pytest-dev/pytest/issues/4393. --- changelog/4393.bugfix.rst | 1 + src/_pytest/cacheprovider.py | 11 +++++++++-- testing/test_cacheprovider.py | 27 +++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 changelog/4393.bugfix.rst diff --git a/changelog/4393.bugfix.rst b/changelog/4393.bugfix.rst new file mode 100644 index 000000000..4bde5aa43 --- /dev/null +++ b/changelog/4393.bugfix.rst @@ -0,0 +1 @@ +Do not create ``.gitignore``/``README.md`` files in existing cache directories. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index d762d867d..5b0b32641 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -108,6 +108,10 @@ class Cache(object): """ path = self._getvaluepath(key) try: + if path.parent.is_dir(): + cache_dir_exists_already = True + else: + cache_dir_exists_already = self._cachedir.exists() path.parent.mkdir(exist_ok=True, parents=True) except (IOError, OSError): self.warn("could not create cache path {path}", path=path) @@ -119,6 +123,7 @@ class Cache(object): else: with f: json.dump(value, f, indent=2, sort_keys=True) + if not cache_dir_exists_already: self._ensure_supporting_files() def _ensure_supporting_files(self): @@ -128,8 +133,10 @@ class Cache(object): if not readme_path.is_file(): readme_path.write_text(README_CONTENT) - msg = u"# created by pytest automatically, do not change\n*" - self._cachedir.joinpath(".gitignore").write_text(msg, encoding="UTF-8") + gitignore_path = self._cachedir.joinpath(".gitignore") + if not gitignore_path.is_file(): + msg = u"# Created by pytest automatically.\n*" + gitignore_path.write_text(msg, encoding="UTF-8") class LFPlugin(object): diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 2b8ca2e18..3de53b59f 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -899,5 +899,28 @@ def test_gitignore(testdir): config = testdir.parseconfig() cache = Cache.for_config(config) cache.set("foo", "bar") - msg = "# created by pytest automatically, do not change\n*" - assert cache._cachedir.joinpath(".gitignore").read_text(encoding="UTF-8") == msg + msg = "# Created by pytest automatically.\n*" + gitignore_path = cache._cachedir.joinpath(".gitignore") + assert gitignore_path.read_text(encoding="UTF-8") == msg + + # Does not overwrite existing/custom one. + gitignore_path.write_text("") + cache.set("something", "else") + assert gitignore_path.read_text(encoding="UTF-8") == "" + + +def test_does_not_create_boilerplate_in_existing_dirs(testdir): + from _pytest.cacheprovider import Cache + + testdir.makeini( + """ + [pytest] + cache_dir = . + """ + ) + config = testdir.parseconfig() + cache = Cache.for_config(config) + cache.set("foo", "bar") + + assert not os.path.exists(".gitignore") + assert not os.path.exists("README.md") From 1568e38997ff6252e079c19e032c74abfe8b3f77 Mon Sep 17 00:00:00 2001 From: "Michael D. Hoyle" Date: Mon, 12 Nov 2018 16:20:15 -0500 Subject: [PATCH 02/18] Use pkg_resources.parse_version in minver check Use pkg_resources.parse_version in minver check Add meself to AUTHORS & changelog Format CHANGELOG --- AUTHORS | 1 + changelog/4315.trivial.rst | 1 + src/_pytest/config/__init__.py | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog/4315.trivial.rst diff --git a/AUTHORS b/AUTHORS index 777eda324..1316f7b8f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -158,6 +158,7 @@ Michael Droettboom Michael Seifert Michal Wajszczuk Mihai Capotă +Mike Hoyle (hoylemd) Mike Lundy Miro Hrončok Nathaniel Waisbrot diff --git a/changelog/4315.trivial.rst b/changelog/4315.trivial.rst new file mode 100644 index 000000000..ee7266aa0 --- /dev/null +++ b/changelog/4315.trivial.rst @@ -0,0 +1 @@ +Use ``pkg_resources.parse_version`` instead of ``LooseVersion`` in minversion check. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 0fc895546..7e24316ce 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -11,10 +11,10 @@ import shlex import sys import types import warnings -from distutils.version import LooseVersion import py import six +from pkg_resources import parse_version from pluggy import HookimplMarker from pluggy import HookspecMarker from pluggy import PluginManager @@ -822,7 +822,7 @@ class Config(object): minver = self.inicfg.get("minversion", None) if minver: - if LooseVersion(minver) > LooseVersion(pytest.__version__): + if parse_version(minver) > parse_version(pytest.__version__): raise pytest.UsageError( "%s:%d: requires pytest-%s, actual pytest-%s'" % ( From fc61bdd9073d86819eb54c8603384a10c626a9d4 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 20 Nov 2018 13:47:40 +0100 Subject: [PATCH 03/18] fix 4425: resolve --basetemp to absolute paths --- changelog/4425.bugfix.rst | 1 + src/_pytest/monkeypatch.py | 6 +++++- src/_pytest/tmpdir.py | 6 ++++-- testing/test_tmpdir.py | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 changelog/4425.bugfix.rst diff --git a/changelog/4425.bugfix.rst b/changelog/4425.bugfix.rst new file mode 100644 index 000000000..7c869cd4c --- /dev/null +++ b/changelog/4425.bugfix.rst @@ -0,0 +1 @@ +Ensure we resolve the absolute path when the given ``--basetemp`` is a relative path. diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index d536b7746..2c81de177 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -13,8 +13,9 @@ import six import pytest from _pytest.fixtures import fixture +from _pytest.pathlib import Path -RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") +RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") @fixture @@ -267,6 +268,9 @@ class MonkeyPatch(object): self._cwd = os.getcwd() if hasattr(path, "chdir"): path.chdir() + elif isinstance(path, Path): + # modern python uses the fspath protocol here LEGACY + os.chdir(str(path)) else: os.chdir(path) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 6287c1705..81430e4f0 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -26,7 +26,9 @@ class TempPathFactory(object): The base directory can be configured using the ``--basetemp`` option.""" - _given_basetemp = attr.ib() + _given_basetemp = attr.ib( + convert=attr.converters.optional(lambda p: Path(p).resolve()) + ) _trace = attr.ib() _basetemp = attr.ib(default=None) @@ -53,7 +55,7 @@ class TempPathFactory(object): """ return base temporary directory. """ if self._basetemp is None: if self._given_basetemp is not None: - basetemp = Path(self._given_basetemp) + basetemp = self._given_basetemp ensure_reset_dir(basetemp) else: from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 38b0672b7..fddece2cf 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -4,6 +4,7 @@ from __future__ import print_function import sys +import attr import six import pytest @@ -25,12 +26,29 @@ def test_ensuretemp(recwarn): assert d1.check(dir=1) +@attr.s +class FakeConfig(object): + basetemp = attr.ib() + trace = attr.ib(default=None) + + @property + def trace(self): + return self + + def get(self, key): + return lambda *k: None + + @property + def option(self): + return self + + class TestTempdirHandler(object): - def test_mktemp(self, testdir): + def test_mktemp(self, tmp_path): + from _pytest.tmpdir import TempdirFactory, TempPathFactory - config = testdir.parseconfig() - config.option.basetemp = testdir.mkdir("hello") + config = FakeConfig(tmp_path) t = TempdirFactory(TempPathFactory.from_config(config)) tmp = t.mktemp("world") assert tmp.relto(t.getbasetemp()) == "world0" @@ -40,6 +58,15 @@ class TestTempdirHandler(object): assert tmp2.relto(t.getbasetemp()).startswith("this") assert tmp2 != tmp + @pytest.mark.issue(4425) + def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch): + from _pytest.tmpdir import TempPathFactory + + monkeypatch.chdir(tmp_path) + config = FakeConfig("hello") + t = TempPathFactory.from_config(config) + assert t.getbasetemp() == (tmp_path / "hello") + class TestConfigTmpdir(object): def test_getbasetemp_custom_removes_old(self, testdir): From f180ab3e69438559df309e8eac563499bde4fba2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 20 Nov 2018 20:08:01 -0200 Subject: [PATCH 04/18] Use os.path.abspath to get absolute path instead of Path.resolve() Unfortunately it seems there is a difference in resolve() behavior depending on the platform --- src/_pytest/tmpdir.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 81430e4f0..937d90c2f 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -27,7 +27,9 @@ class TempPathFactory(object): The base directory can be configured using the ``--basetemp`` option.""" _given_basetemp = attr.ib( - convert=attr.converters.optional(lambda p: Path(p).resolve()) + # using os.path.abspath() to get absolute path instead of resolve() as it + # does not work the same in all platforms + convert=attr.converters.optional(lambda p: Path(os.path.abspath(p))) ) _trace = attr.ib() _basetemp = attr.ib(default=None) From ee4f8c98a9c85773a5ed341c3d0b995ea8c60c68 Mon Sep 17 00:00:00 2001 From: Slam <3lnc.slam@gmail.com> Date: Wed, 21 Nov 2018 14:37:28 +0200 Subject: [PATCH 05/18] Adds note on multiple module marker usage --- doc/en/example/markers.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index cb6368a64..e0682c834 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -271,8 +271,12 @@ You can also set a module level marker:: import pytest pytestmark = pytest.mark.webtest -in which case it will be applied to all functions and -methods defined in the module. +or multiple markers:: + + pytestmark = [pytest.mark.webtest, pytest.mark.slowtest] + +in which case markers will be applied (in left-to-right order) to +all functions and methods defined in the module. .. _`marking individual tests when using parametrize`: From 4f5c153d29eb91a76e8f2aa519684e9710ef6624 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 21 Nov 2018 20:46:08 -0200 Subject: [PATCH 06/18] Fix call to os.path.abspath: the argument might already be a Path instance There's Path.absolute(), but it is not public, see https://bugs.python.org/issue25012. --- src/_pytest/tmpdir.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 937d90c2f..dadb196ea 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -10,6 +10,7 @@ import warnings import attr import py +import six import pytest from .pathlib import ensure_reset_dir @@ -28,8 +29,11 @@ class TempPathFactory(object): _given_basetemp = attr.ib( # using os.path.abspath() to get absolute path instead of resolve() as it - # does not work the same in all platforms - convert=attr.converters.optional(lambda p: Path(os.path.abspath(p))) + # does not work the same in all platforms; there's Path.absolute(), but it is not + # public (see https://bugs.python.org/issue25012) + convert=attr.converters.optional( + lambda p: Path(os.path.abspath(six.text_type(p))) + ) ) _trace = attr.ib() _basetemp = attr.ib(default=None) From f1fe9e41acf6e2099270a931c46cf390cdc6e348 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 21 Nov 2018 20:49:17 -0200 Subject: [PATCH 07/18] Mention PR# in the comment for future reference --- src/_pytest/tmpdir.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index dadb196ea..860c2d4af 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -29,8 +29,8 @@ class TempPathFactory(object): _given_basetemp = attr.ib( # using os.path.abspath() to get absolute path instead of resolve() as it - # does not work the same in all platforms; there's Path.absolute(), but it is not - # public (see https://bugs.python.org/issue25012) + # does not work the same in all platforms (see #4427) + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012) convert=attr.converters.optional( lambda p: Path(os.path.abspath(six.text_type(p))) ) From b3700f61baca9920c421257d1cd8bb00c44d51bc Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 22 Nov 2018 00:15:14 -0800 Subject: [PATCH 08/18] Fix formatting of print() calls --- doc/en/capture.rst | 2 +- doc/en/example/markers.rst | 2 +- doc/en/example/special.rst | 16 +++++------ doc/en/fixture.rst | 16 +++++------ doc/en/funcarg_compare.rst | 4 +-- doc/en/getting-started.rst | 4 +-- doc/en/writing_plugins.rst | 2 +- extra/get_issues.py | 4 +-- testing/acceptance_test.py | 6 ++-- testing/python/fixture.py | 22 +++++++-------- testing/python/integration.py | 2 +- testing/python/raises.py | 2 +- testing/test_capture.py | 52 +++++++++++++++++------------------ testing/test_junitxml.py | 4 +-- testing/test_mark.py | 2 +- testing/test_nose.py | 20 +++++++------- testing/test_runner_xunit.py | 8 +++--- testing/test_terminal.py | 18 ++++++------ testing/test_unittest.py | 12 ++++---- 19 files changed, 99 insertions(+), 99 deletions(-) diff --git a/doc/en/capture.rst b/doc/en/capture.rst index ab86fb55f..87fcbc1a6 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -52,7 +52,7 @@ is that you can use print statements for debugging:: # content of test_module.py def setup_function(function): - print ("setting up %s" % function) + print("setting up %s" % function) def test_func1(): assert True diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index e0682c834..709aef0cd 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -466,7 +466,7 @@ test function. From a conftest file we can read it like this:: def pytest_runtest_setup(item): for mark in item.iter_markers(name='glob'): - print ("glob args=%s kwargs=%s" %(mark.args, mark.kwargs)) + print("glob args=%s kwargs=%s" % (mark.args, mark.kwargs)) sys.stdout.flush() Let's run this without capturing output and see what we get:: diff --git a/doc/en/example/special.rst b/doc/en/example/special.rst index 1fc32f6c8..524ae7883 100644 --- a/doc/en/example/special.rst +++ b/doc/en/example/special.rst @@ -13,7 +13,7 @@ calls it:: @pytest.fixture(scope="session", autouse=True) def callattr_ahead_of_alltests(request): - print ("callattr_ahead_of_alltests called") + print("callattr_ahead_of_alltests called") seen = set([None]) session = request.node for item in session.items: @@ -31,20 +31,20 @@ will be called ahead of running any tests:: class TestHello(object): @classmethod def callme(cls): - print ("callme called!") + print("callme called!") def test_method1(self): - print ("test_method1 called") + print("test_method1 called") def test_method2(self): - print ("test_method1 called") + print("test_method1 called") class TestOther(object): @classmethod def callme(cls): - print ("callme other called") + print("callme other called") def test_other(self): - print ("test other") + print("test other") # works with unittest as well ... import unittest @@ -52,10 +52,10 @@ will be called ahead of running any tests:: class SomeTest(unittest.TestCase): @classmethod def callme(self): - print ("SomeTest callme called") + print("SomeTest callme called") def test_unit1(self): - print ("test_unit1 method called") + print("test_unit1 method called") If you run this without output capturing:: diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 7d9af6fa8..1eed211b0 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -460,7 +460,7 @@ read an optional server URL from the test module which uses our fixture:: server = getattr(request.module, "smtpserver", "smtp.gmail.com") smtp_connection = smtplib.SMTP(server, 587, timeout=5) yield smtp_connection - print ("finalizing %s (%s)" % (smtp_connection, server)) + print("finalizing %s (%s)" % (smtp_connection, server)) smtp_connection.close() We use the ``request.module`` attribute to optionally obtain an @@ -821,23 +821,23 @@ to show the setup/teardown flow:: @pytest.fixture(scope="module", params=["mod1", "mod2"]) def modarg(request): param = request.param - print (" SETUP modarg %s" % param) + print(" SETUP modarg %s" % param) yield param - print (" TEARDOWN modarg %s" % param) + print(" TEARDOWN modarg %s" % param) @pytest.fixture(scope="function", params=[1,2]) def otherarg(request): param = request.param - print (" SETUP otherarg %s" % param) + print(" SETUP otherarg %s" % param) yield param - print (" TEARDOWN otherarg %s" % param) + print(" TEARDOWN otherarg %s" % param) def test_0(otherarg): - print (" RUN test0 with otherarg %s" % otherarg) + print(" RUN test0 with otherarg %s" % otherarg) def test_1(modarg): - print (" RUN test1 with modarg %s" % modarg) + print(" RUN test1 with modarg %s" % modarg) def test_2(otherarg, modarg): - print (" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg)) + print(" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg)) Let's run the tests in verbose mode and with looking at the print-output:: diff --git a/doc/en/funcarg_compare.rst b/doc/en/funcarg_compare.rst index 5403da2f2..4fa9b79ae 100644 --- a/doc/en/funcarg_compare.rst +++ b/doc/en/funcarg_compare.rst @@ -26,9 +26,9 @@ a per-session Database object:: # content of conftest.py class Database(object): def __init__(self): - print ("database instance created") + print("database instance created") def destroy(self): - print ("database instance destroyed") + print("database instance destroyed") def pytest_funcarg__db(request): return request.cached_setup(setup=DataBase, diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 418d4f8cd..56bb300c3 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -138,7 +138,7 @@ Request a unique temporary directory for functional tests # content of test_tmpdir.py def test_needsfiles(tmpdir): - print (tmpdir) + print(tmpdir) assert 0 List the name ``tmpdir`` in the test function signature and ``pytest`` will lookup and call a fixture factory to create the resource before performing the test function call. Before the test runs, ``pytest`` creates a unique-per-test-invocation temporary directory:: @@ -151,7 +151,7 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look tmpdir = local('PYTEST_TMPDIR/test_needsfiles0') def test_needsfiles(tmpdir): - print (tmpdir) + print(tmpdir) > assert 0 E assert 0 diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 527a7263a..68ff4b831 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -73,7 +73,7 @@ sub directory but not for other directories:: a/conftest.py: def pytest_runtest_setup(item): # called for running each test in 'a' directory - print ("setting up", item) + print("setting up", item) a/test_sub.py: def test_sub(): diff --git a/extra/get_issues.py b/extra/get_issues.py index 25bfc3e9a..9407aeded 100644 --- a/extra/get_issues.py +++ b/extra/get_issues.py @@ -65,9 +65,9 @@ def report(issues): print(title) # print() # lines = body.split("\n") - # print ("\n".join(lines[:3])) + # print("\n".join(lines[:3])) # if len(lines) > 3 or len(body) > 240: - # print ("...") + # print("...") print("\n\nFound %s open issues" % len(issues)) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 37ec6d84d..b23cd7ca8 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -206,7 +206,7 @@ class TestGeneralUsage(object): testdir.makeconftest( """ import sys - print ("should not be seen") + print("should not be seen") sys.stderr.write("stder42\\n") """ ) @@ -218,7 +218,7 @@ class TestGeneralUsage(object): def test_conftest_printing_shows_if_error(self, testdir): testdir.makeconftest( """ - print ("should be seen") + print("should be seen") assert 0 """ ) @@ -301,7 +301,7 @@ class TestGeneralUsage(object): def pytest_generate_tests(metafunc): metafunc.addcall({'x': 3}, id='hello-123') def pytest_runtest_setup(item): - print (item.keywords) + print(item.keywords) if 'hello-123' in item.keywords: pytest.skip("hello") assert 0 diff --git a/testing/python/fixture.py b/testing/python/fixture.py index b483fff45..1008bd3d8 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1495,7 +1495,7 @@ class TestFixtureManagerParseFactories(object): return "class" def test_hello(self, item, fm): faclist = fm.getfixturedefs("hello", item.nodeid) - print (faclist) + print(faclist) assert len(faclist) == 3 assert faclist[0].func(item._request) == "conftest" @@ -2040,7 +2040,7 @@ class TestAutouseManagement(object): values.append("step2-%d" % item) def test_finish(): - print (values) + print(values) assert values == ["setup-1", "step1-1", "step2-1", "teardown-1", "setup-2", "step1-2", "step2-2", "teardown-2",] """ @@ -2880,7 +2880,7 @@ class TestFixtureMarker(object): def base(request, fix1): def cleanup_base(): values.append("fin_base") - print ("finalizing base") + print("finalizing base") request.addfinalizer(cleanup_base) def test_begin(): @@ -3480,13 +3480,13 @@ class TestContextManagerFixtureFuncs(object): from test_context import fixture @fixture def arg1(): - print ("setup") + print("setup") yield 1 - print ("teardown") + print("teardown") def test_1(arg1): - print ("test1", arg1) + print("test1", arg1) def test_2(arg1): - print ("test2", arg1) + print("test2", arg1) assert 0 """ ) @@ -3509,13 +3509,13 @@ class TestContextManagerFixtureFuncs(object): from test_context import fixture @fixture(scope="module") def arg1(): - print ("setup") + print("setup") yield 1 - print ("teardown") + print("teardown") def test_1(arg1): - print ("test1", arg1) + print("test1", arg1) def test_2(arg1): - print ("test2", arg1) + print("test2", arg1) """ ) result = testdir.runpytest("-s") diff --git a/testing/python/integration.py b/testing/python/integration.py index a6fb93bfb..79de048c3 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -283,7 +283,7 @@ class TestReRunTests(object): global count, req assert request != req req = request - print ("fix count %s" % count) + print("fix count %s" % count) count += 1 def test_fix(fix): pass diff --git a/testing/python/raises.py b/testing/python/raises.py index a72aeef63..3b94b05f5 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -43,7 +43,7 @@ class TestRaises(object): with pytest.raises(ZeroDivisionError) as excinfo: assert isinstance(excinfo, _pytest._code.ExceptionInfo) 1/0 - print (excinfo) + print(excinfo) assert excinfo.type == ZeroDivisionError assert isinstance(excinfo.value, ZeroDivisionError) diff --git a/testing/test_capture.py b/testing/test_capture.py index 39ddd1f79..47aba70d4 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -107,8 +107,8 @@ def test_capturing_unicode(testdir, method): # taken from issue 227 from nosetests def test_unicode(): import sys - print (sys.stdout) - print (%s) + print(sys.stdout) + print(%s) """ % obj ) @@ -121,7 +121,7 @@ def test_capturing_bytes_in_utf8_encoding(testdir, method): testdir.makepyfile( """ def test_unicode(): - print ('b\\u00f6y') + print('b\\u00f6y') """ ) result = testdir.runpytest("--capture=%s" % method) @@ -131,7 +131,7 @@ def test_capturing_bytes_in_utf8_encoding(testdir, method): def test_collect_capturing(testdir): p = testdir.makepyfile( """ - print ("collect %s failure" % 13) + print("collect %s failure" % 13) import xyz42123 """ ) @@ -144,14 +144,14 @@ class TestPerTestCapturing(object): p = testdir.makepyfile( """ def setup_module(mod): - print ("setup module") + print("setup module") def setup_function(function): - print ("setup " + function.__name__) + print("setup " + function.__name__) def test_func1(): - print ("in func1") + print("in func1") assert 0 def test_func2(): - print ("in func2") + print("in func2") assert 0 """ ) @@ -172,14 +172,14 @@ class TestPerTestCapturing(object): """ import sys def setup_module(func): - print ("module-setup") + print("module-setup") def setup_function(func): - print ("function-setup") + print("function-setup") def test_func(): - print ("in function") + print("in function") assert 0 def teardown_function(func): - print ("in teardown") + print("in teardown") """ ) result = testdir.runpytest(p) @@ -198,9 +198,9 @@ class TestPerTestCapturing(object): p = testdir.makepyfile( """ def test_func1(): - print ("in func1") + print("in func1") def test_func2(): - print ("in func2") + print("in func2") assert 0 """ ) @@ -213,12 +213,12 @@ class TestPerTestCapturing(object): p = testdir.makepyfile( """ def setup_function(function): - print ("setup func1") + print("setup func1") def teardown_function(function): - print ("teardown func1") + print("teardown func1") assert 0 def test_func1(): - print ("in func1") + print("in func1") pass """ ) @@ -238,7 +238,7 @@ class TestPerTestCapturing(object): p = testdir.makepyfile( """ def teardown_module(mod): - print ("teardown module") + print("teardown module") assert 0 def test_func(): pass @@ -259,10 +259,10 @@ class TestPerTestCapturing(object): """\ import sys def test_capturing(): - print (42) + print(42) sys.stderr.write(str(23)) def test_capturing_error(): - print (1) + print(1) sys.stderr.write(str(2)) raise ValueError """ @@ -392,7 +392,7 @@ class TestCaptureFixture(object): reprec = testdir.inline_runsource( """\ def test_hello(capsys): - print (42) + print(42) out, err = capsys.readouterr() assert out.startswith("42") """, @@ -460,7 +460,7 @@ class TestCaptureFixture(object): p = testdir.makepyfile( """\ def test_hello(cap{}): - print ("xxx42xxx") + print("xxx42xxx") assert 0 """.format( method @@ -702,7 +702,7 @@ def test_capture_conftest_runtest_setup(testdir): testdir.makeconftest( """ def pytest_runtest_setup(): - print ("hello19") + print("hello19") """ ) testdir.makepyfile("def test_func(): pass") @@ -737,7 +737,7 @@ def test_capture_early_option_parsing(testdir): testdir.makeconftest( """ def pytest_runtest_setup(): - print ("hello19") + print("hello19") """ ) testdir.makepyfile("def test_func(): pass") @@ -1302,14 +1302,14 @@ def test_capturing_and_logging_fundamentals(testdir, method): logging.warn("hello1") outerr = cap.readouterr() - print ("suspend, captured %%s" %%(outerr,)) + print("suspend, captured %%s" %%(outerr,)) logging.warn("hello2") cap.pop_outerr_to_orig() logging.warn("hello3") outerr = cap.readouterr() - print ("suspend2, captured %%s" %% (outerr,)) + print("suspend2, captured %%s" %% (outerr,)) """ % (method,) ) diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index db308b688..c9dc39f82 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -371,7 +371,7 @@ class TestPython(object): import sys def test_fail(): - print ("hello-stdout") + print("hello-stdout") sys.stderr.write("hello-stderr\\n") logging.info('info msg') logging.warning('warning msg') @@ -589,7 +589,7 @@ class TestPython(object): """ # coding: latin1 def test_hello(): - print (%r) + print(%r) assert 0 """ % value diff --git a/testing/test_mark.py b/testing/test_mark.py index 1f50045c5..e32bcc395 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -189,7 +189,7 @@ def test_ini_markers(testdir): """ def test_markers(pytestconfig): markers = pytestconfig.getini("markers") - print (markers) + print(markers) assert len(markers) >= 2 assert markers[0].startswith("a1:") assert markers[1].startswith("a2:") diff --git a/testing/test_nose.py b/testing/test_nose.py index 0af591b36..e4db46802 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -71,11 +71,11 @@ def test_nose_setup_func(testdir): @with_setup(my_setup, my_teardown) def test_hello(): - print (values) + print(values) assert values == [1] def test_world(): - print (values) + print(values) assert values == [1,2] """ @@ -95,11 +95,11 @@ def test_nose_setup_func_failure(testdir): @with_setup(my_setup, my_teardown) def test_hello(): - print (values) + print(values) assert values == [1] def test_world(): - print (values) + print(values) assert values == [1,2] """ @@ -147,11 +147,11 @@ def test_nose_setup_partial(testdir): my_teardown_partial = partial(my_teardown, 2) def test_hello(): - print (values) + print(values) assert values == [1] def test_world(): - print (values) + print(values) assert values == [1,2] test_hello.setup = my_setup_partial @@ -202,21 +202,21 @@ def test_nose_test_generator_fixtures(testdir): class TestClass(object): def setup(self): - print ("setup called in %s" % self) + print("setup called in %s" % self) self.called = ['setup'] def teardown(self): - print ("teardown called in %s" % self) + print("teardown called in %s" % self) eq_(self.called, ['setup']) self.called.append('teardown') def test(self): - print ("test called in %s" % self) + print("test called in %s" % self) for i in range(0, 5): yield self.check, i def check(self, i): - print ("check called in %s" % self) + print("check called in %s" % self) expect = ['setup'] #for x in range(0, i): # expect.append('setup') diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index 31937c919..b0844dc1c 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -202,21 +202,21 @@ def test_func_generator_setup(testdir): import sys def setup_module(mod): - print ("setup_module") + print("setup_module") mod.x = [] def setup_function(fun): - print ("setup_function") + print("setup_function") x.append(1) def teardown_function(fun): - print ("teardown_function") + print("teardown_function") x.pop() def test_one(): assert x == [1] def check(): - print ("check") + print("check") sys.stderr.write("e\\n") assert x == [1] yield check diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 9c2f93ed1..a028f9c22 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -365,7 +365,7 @@ class TestFixtureReporting(object): testdir.makepyfile( """ def setup_function(function): - print ("setup func") + print("setup func") assert 0 def test_nada(): pass @@ -389,7 +389,7 @@ class TestFixtureReporting(object): def test_nada(): pass def teardown_function(function): - print ("teardown func") + print("teardown func") assert 0 """ ) @@ -412,7 +412,7 @@ class TestFixtureReporting(object): assert 0, "failingfunc" def teardown_function(function): - print ("teardown func") + print("teardown func") assert False """ ) @@ -436,13 +436,13 @@ class TestFixtureReporting(object): testdir.makepyfile( """ def setup_function(function): - print ("setup func") + print("setup func") def test_fail(): assert 0, "failingfunc" def teardown_function(function): - print ("teardown func") + print("teardown func") """ ) result = testdir.runpytest() @@ -854,7 +854,7 @@ class TestGenericReporting(object): def g(): raise IndexError def test_func(): - print (6*7) + print(6*7) g() # --calling-- """ ) @@ -863,9 +863,9 @@ class TestGenericReporting(object): result = testdir.runpytest("--tb=%s" % tbopt) s = result.stdout.str() if tbopt == "long": - assert "print (6*7)" in s + assert "print(6*7)" in s else: - assert "print (6*7)" not in s + assert "print(6*7)" not in s if tbopt != "no": assert "--calling--" in s assert "IndexError" in s @@ -881,7 +881,7 @@ class TestGenericReporting(object): def g(): raise IndexError def test_func1(): - print (6*7) + print(6*7) g() # --calling-- def test_func2(): assert 0, "hello" diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 28ce90a3d..2c60cd271 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -244,7 +244,7 @@ def test_setup_failure_is_shown(testdir): def setUp(self): assert 0, "down1" def test_method(self): - print ("never42") + print("never42") xyz """ ) @@ -610,14 +610,14 @@ def test_djangolike_testcase(testdir): class DjangoLikeTestCase(TestCase): def setUp(self): - print ("setUp()") + print("setUp()") def test_presetup_has_been_run(self): - print ("test_thing()") + print("test_thing()") self.assertTrue(hasattr(self, 'was_presetup')) def tearDown(self): - print ("tearDown()") + print("tearDown()") def __call__(self, result=None): try: @@ -639,11 +639,11 @@ def test_djangolike_testcase(testdir): return def _pre_setup(self): - print ("_pre_setup()") + print("_pre_setup()") self.was_presetup = True def _post_teardown(self): - print ("_post_teardown()") + print("_post_teardown()") """ ) result = testdir.runpytest("-s") From 664257c7a3917c88375db6c44af5ea79ccb115ed Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 22 Nov 2018 00:20:13 -0800 Subject: [PATCH 09/18] Color the setup ERROR red --- src/_pytest/terminal.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index f19325088..94ad32e0f 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -786,8 +786,7 @@ class TerminalReporter(object): self.write_line(line) else: msg = self._getfailureheadline(rep) - markup = {"red": True, "bold": True} - self.write_sep("_", msg, **markup) + self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) for report in self.getreports(""): if report.nodeid == rep.nodeid and report.when == "teardown": @@ -808,7 +807,7 @@ class TerminalReporter(object): msg = "ERROR at setup of " + msg elif rep.when == "teardown": msg = "ERROR at teardown of " + msg - self.write_sep("_", msg) + self.write_sep("_", msg, red=True, bold=True) self._outrep_summary(rep) def _outrep_summary(self, rep): From 5f1d69207260297738ff4dd96687fe049d2963a3 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 22 Nov 2018 16:10:12 +0100 Subject: [PATCH 10/18] use Path.resolve in test to sort out osx temporary folder being a symlink --- testing/test_tmpdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index fddece2cf..6040d9444 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -65,7 +65,7 @@ class TestTempdirHandler(object): monkeypatch.chdir(tmp_path) config = FakeConfig("hello") t = TempPathFactory.from_config(config) - assert t.getbasetemp() == (tmp_path / "hello") + assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve() class TestConfigTmpdir(object): From aa765cf8c243dec9502ecf3379b2e6ed26c274d7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 22 Nov 2018 14:44:01 -0200 Subject: [PATCH 11/18] Adjust stacklevel of "config" warnings Related to #4439 --- src/_pytest/assertion/rewrite.py | 1 + src/_pytest/cacheprovider.py | 4 +++- src/_pytest/config/__init__.py | 2 +- src/_pytest/config/findpaths.py | 5 ++++- src/_pytest/resultlog.py | 2 +- src/_pytest/warnings.py | 5 +++-- testing/test_warnings.py | 2 +- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 7b9aa5006..d1231b774 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -270,6 +270,7 @@ class AssertionRewritingHook(object): _issue_config_warning( PytestWarning("Module already imported so cannot be rewritten: %s" % name), self.config, + stacklevel=5, ) def load_module(self, name): diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index d762d867d..4e51af771 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -56,7 +56,9 @@ class Cache(object): from _pytest.warning_types import PytestWarning _issue_config_warning( - PytestWarning(fmt.format(**args) if args else fmt), self._config + PytestWarning(fmt.format(**args) if args else fmt), + self._config, + stacklevel=3, ) def makedir(self, name): diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 26a0973ca..562b50c38 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -191,7 +191,7 @@ def _prepareconfig(args=None, plugins=None): if warning: from _pytest.warnings import _issue_config_warning - _issue_config_warning(warning, config=config) + _issue_config_warning(warning, config=config, stacklevel=4) return pluginmanager.hook.pytest_cmdline_parse( pluginmanager=pluginmanager, args=args ) diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 4f371ec7f..a9d674e77 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -42,6 +42,7 @@ def getcfg(args, config=None): CFG_PYTEST_SECTION.format(filename=inibasename) ), config=config, + stacklevel=2, ) return base, p, iniconfig["pytest"] if ( @@ -116,7 +117,9 @@ def determine_setup(inifile, args, rootdir_cmd_arg=None, config=None): # TODO: [pytest] section in *.cfg files is deprecated. Need refactoring once # the deprecation expires. _issue_config_warning( - CFG_PYTEST_SECTION.format(filename=str(inifile)), config + CFG_PYTEST_SECTION.format(filename=str(inifile)), + config, + stacklevel=2, ) break except KeyError: diff --git a/src/_pytest/resultlog.py b/src/_pytest/resultlog.py index 3efdbea6e..ab2d0f98b 100644 --- a/src/_pytest/resultlog.py +++ b/src/_pytest/resultlog.py @@ -36,7 +36,7 @@ def pytest_configure(config): from _pytest.deprecated import RESULT_LOG from _pytest.warnings import _issue_config_warning - _issue_config_warning(RESULT_LOG, config) + _issue_config_warning(RESULT_LOG, config, stacklevel=2) def pytest_unconfigure(config): diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 9de9d01d5..e3e206933 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -160,7 +160,7 @@ def pytest_terminal_summary(terminalreporter): yield -def _issue_config_warning(warning, config): +def _issue_config_warning(warning, config, stacklevel): """ This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage: at this point the actual options might not have been set, so we manually trigger the pytest_warning_captured @@ -168,10 +168,11 @@ def _issue_config_warning(warning, config): :param warning: the warning instance. :param config: + :param stacklevel: stacklevel forwarded to warnings.warn """ with warnings.catch_warnings(record=True) as records: warnings.simplefilter("always", type(warning)) - warnings.warn(warning, stacklevel=2) + warnings.warn(warning, stacklevel=stacklevel) config.hook.pytest_warning_captured.call_historic( kwargs=dict(warning_message=records[0], when="config", item=None) ) diff --git a/testing/test_warnings.py b/testing/test_warnings.py index d79e956e3..53d9c71cd 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -310,7 +310,7 @@ def test_warning_captured_hook(testdir): """ from _pytest.warnings import _issue_config_warning def pytest_configure(config): - _issue_config_warning(UserWarning("config warning"), config) + _issue_config_warning(UserWarning("config warning"), config, stacklevel=2) """ ) testdir.makepyfile( From d471ecc4d8f0fea5819374bc75b2f2b7c3860c69 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 22 Nov 2018 14:45:50 -0200 Subject: [PATCH 12/18] Add changelog entry --- changelog/4440.trivial.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/4440.trivial.rst diff --git a/changelog/4440.trivial.rst b/changelog/4440.trivial.rst new file mode 100644 index 000000000..7187d664f --- /dev/null +++ b/changelog/4440.trivial.rst @@ -0,0 +1 @@ +Adjust the stack level of some internal pytest warnings. From b71bd9b300b6dbab25adcc4a0d9db14763011e72 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 22 Nov 2018 20:43:58 +0100 Subject: [PATCH 13/18] fix #4386 - handle uninitialized exceptioninfo in repr/str --- changelog/4386.bugfix.rst | 1 + src/_pytest/_code/code.py | 15 +++++++++++---- testing/python/raises.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 changelog/4386.bugfix.rst diff --git a/changelog/4386.bugfix.rst b/changelog/4386.bugfix.rst new file mode 100644 index 000000000..0367f3ddc --- /dev/null +++ b/changelog/4386.bugfix.rst @@ -0,0 +1 @@ +Handle uninitialized exceptioninfo in repr/str. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index d06e24f00..26973c4e1 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -425,7 +425,10 @@ class ExceptionInfo(object): self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self)) def __repr__(self): - return "" % (self.typename, len(self.traceback)) + try: + return "" % (self.typename, len(self.traceback)) + except AttributeError: + return "" def exconly(self, tryshort=False): """ return the exception as a string @@ -513,9 +516,13 @@ class ExceptionInfo(object): return fmt.repr_excinfo(self) def __str__(self): - entry = self.traceback[-1] - loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) - return str(loc) + try: + entry = self.traceback[-1] + except AttributeError: + return repr(self) + else: + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) def __unicode__(self): entry = self.traceback[-1] diff --git a/testing/python/raises.py b/testing/python/raises.py index a72aeef63..527d688a2 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -33,6 +33,18 @@ class TestRaises(object): except pytest.raises.Exception: pass + def test_raises_repr_inflight(self): + with pytest.raises(RuntimeError) as excinfo: + # this test prints the inflight uninitialized object + # using repr and str as well as pprint to demonstrate + # it works + print(str(excinfo)) + print(repr(excinfo)) + import pprint + + pprint.pprint(excinfo) + raise RuntimeError(1) + def test_raises_as_contextmanager(self, testdir): testdir.makepyfile( """ From 9ae8429a21ed83bb668c94023e933fa4112d3831 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 22 Nov 2018 20:24:46 -0200 Subject: [PATCH 14/18] Use a more specific exception type in test_raises_repr_inflight As requested during review --- testing/python/raises.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/testing/python/raises.py b/testing/python/raises.py index 527d688a2..f2c44fcf8 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -34,7 +34,12 @@ class TestRaises(object): pass def test_raises_repr_inflight(self): - with pytest.raises(RuntimeError) as excinfo: + """Ensure repr() on an exception info inside a pytest.raises with block works (#4386)""" + + class E(Exception): + pass + + with pytest.raises(E) as excinfo: # this test prints the inflight uninitialized object # using repr and str as well as pprint to demonstrate # it works @@ -43,7 +48,7 @@ class TestRaises(object): import pprint pprint.pprint(excinfo) - raise RuntimeError(1) + raise E() def test_raises_as_contextmanager(self, testdir): testdir.makepyfile( From c5c728c8bcbc07268874c8944ee956fae9c1350c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 22 Nov 2018 21:38:33 -0200 Subject: [PATCH 15/18] Fix test/improve tests a bit in py27 --- testing/test_cacheprovider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 3de53b59f..30fe23aeb 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -904,9 +904,9 @@ def test_gitignore(testdir): assert gitignore_path.read_text(encoding="UTF-8") == msg # Does not overwrite existing/custom one. - gitignore_path.write_text("") + gitignore_path.write_text(u"custom") cache.set("something", "else") - assert gitignore_path.read_text(encoding="UTF-8") == "" + assert gitignore_path.read_text(encoding="UTF-8") == "custom" def test_does_not_create_boilerplate_in_existing_dirs(testdir): @@ -922,5 +922,6 @@ def test_does_not_create_boilerplate_in_existing_dirs(testdir): cache = Cache.for_config(config) cache.set("foo", "bar") + assert os.path.isdir("v") # cache contents assert not os.path.exists(".gitignore") assert not os.path.exists("README.md") From 0cf45ee18a48061163cfd82c1edf1d6292d2a2a4 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 15 Nov 2018 17:05:56 +0100 Subject: [PATCH 16/18] Display "short test summary info" after (main) warnings again Fixes https://github.com/pytest-dev/pytest/issues/3952. --- changelog/3952.bugfix.rst | 1 + src/_pytest/terminal.py | 16 +++++++++++++--- testing/test_terminal.py | 20 ++++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 changelog/3952.bugfix.rst diff --git a/changelog/3952.bugfix.rst b/changelog/3952.bugfix.rst new file mode 100644 index 000000000..e999cdded --- /dev/null +++ b/changelog/3952.bugfix.rst @@ -0,0 +1 @@ +Display warnings before "short test summary info" again, but still later warnings in the end. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 94ad32e0f..3c9abac8a 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -647,9 +647,11 @@ class TerminalReporter(object): def pytest_terminal_summary(self): self.summary_errors() self.summary_failures() - yield self.summary_warnings() + yield self.summary_passes() + # Display any extra warnings from teardown here (if any). + self.summary_warnings() def pytest_keyboard_interrupt(self, excinfo): self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) @@ -726,11 +728,19 @@ class TerminalReporter(object): if not all_warnings: return + final = hasattr(self, "_already_displayed_warnings") + if final: + warnings = all_warnings[self._already_displayed_warnings :] + else: + warnings = all_warnings + self._already_displayed_warnings = len(warnings) + grouped = itertools.groupby( - all_warnings, key=lambda wr: wr.get_location(self.config) + warnings, key=lambda wr: wr.get_location(self.config) ) - self.write_sep("=", "warnings summary", yellow=True, bold=False) + title = "warnings summary (final)" if final else "warnings summary" + self.write_sep("=", title, yellow=True, bold=False) for location, warning_records in grouped: # legacy warnings show their location explicitly, while standard warnings look better without # it because the location is already formatted into the message diff --git a/testing/test_terminal.py b/testing/test_terminal.py index a028f9c22..f1bfa81f2 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1074,11 +1074,27 @@ def test_terminal_summary_warnings_are_displayed(testdir): warnings.warn(UserWarning('internal warning')) """ ) - result = testdir.runpytest() + testdir.makepyfile( + """ + def test_failure(): + import warnings + warnings.warn("warning_from_" + "test") + assert 0 + """ + ) + result = testdir.runpytest("-ra") result.stdout.fnmatch_lines( - ["*conftest.py:3:*internal warning", "*== 1 warnings in *"] + [ + "*= warnings summary =*", + "*warning_from_test*", + "*= short test summary info =*", + "*= warnings summary (final) =*", + "*conftest.py:3:*internal warning", + "*== 1 failed, 2 warnings in *", + ] ) assert "None" not in result.stdout.str() + assert result.stdout.str().count("warning_from_test") == 1 @pytest.mark.parametrize( From 16b15af6245e38624679fcdc86a4f40d0ffec294 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 23 Nov 2018 20:09:57 +0000 Subject: [PATCH 17/18] Preparing release version 4.0.1 --- CHANGELOG.rst | 37 +++++++++++++++++++++++++++++ changelog/3952.bugfix.rst | 1 - changelog/4315.trivial.rst | 1 - changelog/4386.bugfix.rst | 1 - changelog/4393.bugfix.rst | 1 - changelog/4400.bugfix.rst | 1 - changelog/4405.bugfix.rst | 1 - changelog/4412.bugfix.rst | 1 - changelog/4425.bugfix.rst | 1 - changelog/4440.trivial.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-4.0.1.rst | 23 ++++++++++++++++++ doc/en/assert.rst | 4 ++-- doc/en/cache.rst | 6 ++--- doc/en/capture.rst | 2 +- doc/en/doctest.rst | 2 +- doc/en/example/markers.rst | 28 +++++++++++----------- doc/en/example/nonpython.rst | 6 ++--- doc/en/example/parametrize.rst | 12 +++++----- doc/en/example/pythoncollection.rst | 6 ++--- doc/en/example/reportingdemo.rst | 2 +- doc/en/example/simple.rst | 22 ++++++++--------- doc/en/fixture.rst | 12 +++++----- doc/en/getting-started.rst | 4 ++-- doc/en/index.rst | 2 +- doc/en/parametrize.rst | 4 ++-- doc/en/skipping.rst | 2 +- doc/en/tmpdir.rst | 4 ++-- doc/en/unittest.rst | 2 +- doc/en/usage.rst | 4 ++-- doc/en/warnings.rst | 6 ++++- doc/en/writing_plugins.rst | 4 +++- 32 files changed, 131 insertions(+), 73 deletions(-) delete mode 100644 changelog/3952.bugfix.rst delete mode 100644 changelog/4315.trivial.rst delete mode 100644 changelog/4386.bugfix.rst delete mode 100644 changelog/4393.bugfix.rst delete mode 100644 changelog/4400.bugfix.rst delete mode 100644 changelog/4405.bugfix.rst delete mode 100644 changelog/4412.bugfix.rst delete mode 100644 changelog/4425.bugfix.rst delete mode 100644 changelog/4440.trivial.rst create mode 100644 doc/en/announce/release-4.0.1.rst diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4c3d2b249..8e0ba82e4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,43 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 4.0.1 (2018-11-23) +========================= + +Bug Fixes +--------- + +- `#3952 `_: Display warnings before "short test summary info" again, but still later warnings in the end. + + +- `#4386 `_: Handle uninitialized exceptioninfo in repr/str. + + +- `#4393 `_: Do not create ``.gitignore``/``README.md`` files in existing cache directories. + + +- `#4400 `_: Rearrange warning handling for the yield test errors so the opt-out in 4.0.x correctly works. + + +- `#4405 `_: Fix collection of testpaths with ``--pyargs``. + + +- `#4412 `_: Fix assertion rewriting involving ``Starred`` + side-effects. + + +- `#4425 `_: Ensure we resolve the absolute path when the given ``--basetemp`` is a relative path. + + + +Trivial/Internal Changes +------------------------ + +- `#4315 `_: Use ``pkg_resources.parse_version`` instead of ``LooseVersion`` in minversion check. + + +- `#4440 `_: Adjust the stack level of some internal pytest warnings. + + pytest 4.0.0 (2018-11-13) ========================= diff --git a/changelog/3952.bugfix.rst b/changelog/3952.bugfix.rst deleted file mode 100644 index e999cdded..000000000 --- a/changelog/3952.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Display warnings before "short test summary info" again, but still later warnings in the end. diff --git a/changelog/4315.trivial.rst b/changelog/4315.trivial.rst deleted file mode 100644 index ee7266aa0..000000000 --- a/changelog/4315.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Use ``pkg_resources.parse_version`` instead of ``LooseVersion`` in minversion check. diff --git a/changelog/4386.bugfix.rst b/changelog/4386.bugfix.rst deleted file mode 100644 index 0367f3ddc..000000000 --- a/changelog/4386.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Handle uninitialized exceptioninfo in repr/str. diff --git a/changelog/4393.bugfix.rst b/changelog/4393.bugfix.rst deleted file mode 100644 index 4bde5aa43..000000000 --- a/changelog/4393.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Do not create ``.gitignore``/``README.md`` files in existing cache directories. diff --git a/changelog/4400.bugfix.rst b/changelog/4400.bugfix.rst deleted file mode 100644 index eb0df7eca..000000000 --- a/changelog/4400.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Rearrange warning handling for the yield test errors so the opt-out in 4.0.x correctly works. diff --git a/changelog/4405.bugfix.rst b/changelog/4405.bugfix.rst deleted file mode 100644 index ac05091c1..000000000 --- a/changelog/4405.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix collection of testpaths with ``--pyargs``. diff --git a/changelog/4412.bugfix.rst b/changelog/4412.bugfix.rst deleted file mode 100644 index 7a28b6108..000000000 --- a/changelog/4412.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix assertion rewriting involving ``Starred`` + side-effects. diff --git a/changelog/4425.bugfix.rst b/changelog/4425.bugfix.rst deleted file mode 100644 index 7c869cd4c..000000000 --- a/changelog/4425.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure we resolve the absolute path when the given ``--basetemp`` is a relative path. diff --git a/changelog/4440.trivial.rst b/changelog/4440.trivial.rst deleted file mode 100644 index 7187d664f..000000000 --- a/changelog/4440.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Adjust the stack level of some internal pytest warnings. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 504d63484..4120ccfc9 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-4.0.1 release-4.0.0 release-3.10.1 release-3.10.0 diff --git a/doc/en/announce/release-4.0.1.rst b/doc/en/announce/release-4.0.1.rst new file mode 100644 index 000000000..31b222c03 --- /dev/null +++ b/doc/en/announce/release-4.0.1.rst @@ -0,0 +1,23 @@ +pytest-4.0.1 +======================================= + +pytest 4.0.1 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/latest/changelog.html. + +Thanks to all who contributed to this release, among them: + +* Anthony Sottile +* Bruno Oliveira +* Daniel Hahler +* Michael D. Hoyle +* Ronny Pfannschmidt +* Slam + + +Happy testing, +The pytest Development Team diff --git a/doc/en/assert.rst b/doc/en/assert.rst index d3deeb503..01767225f 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -26,7 +26,7 @@ you will see the return value of the function call:: $ pytest test_assert1.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -169,7 +169,7 @@ if you run this module:: $ pytest test_assert2.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item diff --git a/doc/en/cache.rst b/doc/en/cache.rst index 4a917d45a..4a493959a 100644 --- a/doc/en/cache.rst +++ b/doc/en/cache.rst @@ -76,7 +76,7 @@ If you then run it with ``--lf``:: $ pytest --lf =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 50 items / 48 deselected run-last-failure: rerun previous 2 failures @@ -117,7 +117,7 @@ of ``FF`` and dots):: $ pytest --ff =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 50 items run-last-failure: rerun previous 2 failures first @@ -236,7 +236,7 @@ You can always peek at the content of the cache using the $ pytest --cache-show =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: cachedir: $REGENDOC_TMPDIR/.pytest_cache ------------------------------- cache values ------------------------------- diff --git a/doc/en/capture.rst b/doc/en/capture.rst index 87fcbc1a6..a3b7d4d5e 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -65,7 +65,7 @@ of the failing function and hide the other one:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index 9488ee826..0030d8df8 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -62,7 +62,7 @@ then you can just invoke ``pytest`` without command line options:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 1 item diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index 709aef0cd..bcd42616b 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:: $ pytest -v -m webtest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 3 deselected @@ -44,7 +44,7 @@ Or the inverse, running all tests except the webtest ones:: $ pytest -v -m "not webtest" =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 1 deselected @@ -64,7 +64,7 @@ tests based on their module, class, method, or function name:: $ pytest -v test_server.py::TestClass::test_method =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 1 item @@ -77,7 +77,7 @@ You can also select on the class:: $ pytest -v test_server.py::TestClass =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 1 item @@ -90,7 +90,7 @@ Or select multiple nodes:: $ pytest -v test_server.py::TestClass test_server.py::test_send_http =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -128,7 +128,7 @@ select tests based on their names:: $ pytest -v -k http # running with the above defined example module =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 3 deselected @@ -141,7 +141,7 @@ And you can also run all tests except the ones that match the keyword:: $ pytest -k "not send_http" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 1 deselected @@ -156,7 +156,7 @@ Or to select "http" and "quick" tests:: $ pytest -k "http or quick" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items / 2 deselected @@ -355,7 +355,7 @@ the test needs:: $ pytest -E stage2 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -367,7 +367,7 @@ and here is one that specifies exactly the environment needed:: $ pytest -E stage1 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -528,7 +528,7 @@ then you will see two tests skipped and two executed tests as expected:: $ pytest -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -542,7 +542,7 @@ Note that if you specify a platform via the marker-command line option like this $ pytest -m linux =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items / 3 deselected @@ -593,7 +593,7 @@ We can now use the ``-m option`` to select one set:: $ pytest -m interface --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items / 2 deselected @@ -614,7 +614,7 @@ or to select both "event" and "interface" tests:: $ pytest -m "interface or event" --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items / 1 deselected diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index bda15065a..bab89f7b8 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -27,7 +27,7 @@ now execute the test specification:: nonpython $ pytest test_simple.yml =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collected 2 items @@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode:: nonpython $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collecting ... collected 2 items @@ -81,7 +81,7 @@ interesting to just look at the collection tree:: nonpython $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collected 2 items diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index dcd59cfb3..b983b52b5 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -139,7 +139,7 @@ objects, they are still using the default pytest representation:: $ pytest test_time.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 8 items @@ -195,7 +195,7 @@ this is a fully self-contained example which you can run with:: $ pytest test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -208,7 +208,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia $ pytest --collect-only test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -272,7 +272,7 @@ Let's first see how it looks like at collection time:: $ pytest test_backends.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -333,7 +333,7 @@ The result of this test will be successful:: $ pytest test_indirect_list.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -460,7 +460,7 @@ If you run this with reporting for skips enabled:: $ pytest -rs test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 9a439cbae..0c1a2540b 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -127,7 +127,7 @@ The test collection would look like this:: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 2 items @@ -180,7 +180,7 @@ You can always peek at the collection tree without running tests like this:: . $ pytest --collect-only pythoncollection.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 3 items @@ -248,7 +248,7 @@ file will be left out:: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 0 items diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 35ec7b6cc..fceb83dfc 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -11,7 +11,7 @@ get on the terminal - we are working on that):: assertion $ pytest failure_demo.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR/assertion, inifile: collected 42 items diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 2edaf9a74..fa93a77f3 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -121,7 +121,7 @@ directory with the above conftest.py:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -179,7 +179,7 @@ and when running it will see a skipped "slow" test:: $ pytest -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -193,7 +193,7 @@ Or run it including the ``slow`` marked test:: $ pytest --runslow =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -331,7 +331,7 @@ which will add the string to the test header accordingly:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y project deps: mylib-1.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -357,7 +357,7 @@ which will add info only when run with "--v":: $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache info1: did you know that ... did you? @@ -370,7 +370,7 @@ and nothing when run plainly:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -407,7 +407,7 @@ Now we can profile which test functions execute the slowest:: $ pytest --durations=3 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items @@ -479,7 +479,7 @@ If we run this:: $ pytest -rx =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -560,7 +560,7 @@ We can run this:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 7 items @@ -671,7 +671,7 @@ and run them:: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -770,7 +770,7 @@ and run it:: $ pytest -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 1eed211b0..2fb3d4b56 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -70,7 +70,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this:: $ pytest test_smtpsimple.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -208,7 +208,7 @@ inspect what is going on and can now run the tests:: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -690,7 +690,7 @@ Running the above tests results in the following test IDs being used:: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 10 items @@ -732,7 +732,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:: $ pytest test_fixture_marks.py -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 3 items @@ -775,7 +775,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ pytest -v test_appsetup.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -844,7 +844,7 @@ Let's run the tests in verbose mode and with looking at the print-output:: $ pytest -v -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6 cachedir: .pytest_cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 8 items diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 56bb300c3..97ae2abdc 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -27,7 +27,7 @@ Install ``pytest`` 2. Check that you installed the correct version:: $ pytest --version - This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py + This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py .. _`simpletest`: @@ -47,7 +47,7 @@ That’s it. You can now execute the test function:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item diff --git a/doc/en/index.rst b/doc/en/index.rst index 6a382e571..510df4a09 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -26,7 +26,7 @@ To execute it:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 90ce4ffc6..d3bce1b58 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -54,7 +54,7 @@ them in turn:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items @@ -103,7 +103,7 @@ Let's run this:: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index f705422d8..075886a96 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -327,7 +327,7 @@ Running it with the report-on-xfail option gives this output:: example $ pytest -rx xfail_demo.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR/example, inifile: collected 7 items diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index 21bdcdd6a..12a3e5641 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -39,7 +39,7 @@ Running this would result in a passed test except for the last $ pytest test_tmp_path.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -99,7 +99,7 @@ Running this would result in a passed test except for the last $ pytest test_tmpdir.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item diff --git a/doc/en/unittest.rst b/doc/en/unittest.rst index 46a9ee372..5a6561d4b 100644 --- a/doc/en/unittest.rst +++ b/doc/en/unittest.rst @@ -126,7 +126,7 @@ the ``self.db`` values in the traceback:: $ pytest test_unittest_db.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 08682d614..43f20394f 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -154,7 +154,7 @@ Example:: $ pytest -ra =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -177,7 +177,7 @@ More than one character can be used, so for example to only see failed and skipp $ pytest -rfs =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 31bd74695..1317f020f 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -22,7 +22,7 @@ Running pytest now produces this output:: $ pytest test_show_warnings.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item @@ -33,6 +33,8 @@ Running pytest now produces this output:: $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2 warnings.warn(UserWarning("api v1, should use functions from v2")) + -- Docs: https://docs.pytest.org/en/latest/warnings.html + ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html =================== 1 passed, 1 warnings in 0.12 seconds =================== @@ -356,6 +358,8 @@ defines an ``__init__`` constructor, as this prevents the class from being insta $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor class Test: + -- Docs: https://docs.pytest.org/en/latest/warnings.html + ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html 1 warnings in 0.12 seconds diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 68ff4b831..e35577bf8 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -411,7 +411,7 @@ additionally it is possible to copy examples for an example folder before runnin $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 2 items @@ -422,6 +422,8 @@ additionally it is possible to copy examples for an example folder before runnin $REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time testdir.copy_example("test_example.py") + -- Docs: https://docs.pytest.org/en/latest/warnings.html + ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html =================== 2 passed, 1 warnings in 0.12 seconds =================== From be3b8fc9c1417243b9567bd31992c0c42f0bf898 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 23 Nov 2018 22:39:05 +0100 Subject: [PATCH 18/18] Fix warnings summary header appearing twice Ref: https://github.com/pytest-dev/pytest/pull/4450#discussion_r236017645 Ref: https://github.com/pytest-dev/pytest/pull/4399 --- doc/en/warnings.rst | 4 ---- doc/en/writing_plugins.rst | 2 -- src/_pytest/terminal.py | 2 ++ testing/test_terminal.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 1317f020f..182aae4fa 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -33,8 +33,6 @@ Running pytest now produces this output:: $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2 warnings.warn(UserWarning("api v1, should use functions from v2")) - -- Docs: https://docs.pytest.org/en/latest/warnings.html - ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html =================== 1 passed, 1 warnings in 0.12 seconds =================== @@ -358,8 +356,6 @@ defines an ``__init__`` constructor, as this prevents the class from being insta $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor class Test: - -- Docs: https://docs.pytest.org/en/latest/warnings.html - ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html 1 warnings in 0.12 seconds diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index e35577bf8..bee3d6102 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -422,8 +422,6 @@ additionally it is possible to copy examples for an example folder before runnin $REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time testdir.copy_example("test_example.py") - -- Docs: https://docs.pytest.org/en/latest/warnings.html - ========================= warnings summary (final) ========================= -- Docs: https://docs.pytest.org/en/latest/warnings.html =================== 2 passed, 1 warnings in 0.12 seconds =================== diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 3c9abac8a..6f3893653 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -734,6 +734,8 @@ class TerminalReporter(object): else: warnings = all_warnings self._already_displayed_warnings = len(warnings) + if not warnings: + return grouped = itertools.groupby( warnings, key=lambda wr: wr.get_location(self.config) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index f1bfa81f2..86ec1cd07 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1094,7 +1094,34 @@ def test_terminal_summary_warnings_are_displayed(testdir): ] ) assert "None" not in result.stdout.str() - assert result.stdout.str().count("warning_from_test") == 1 + stdout = result.stdout.str() + assert stdout.count("warning_from_test") == 1 + assert stdout.count("=== warnings summary ") == 2 + + +@pytest.mark.filterwarnings("default") +def test_terminal_summary_warnings_header_once(testdir): + testdir.makepyfile( + """ + def test_failure(): + import warnings + warnings.warn("warning_from_" + "test") + assert 0 + """ + ) + result = testdir.runpytest("-ra") + result.stdout.fnmatch_lines( + [ + "*= warnings summary =*", + "*warning_from_test*", + "*= short test summary info =*", + "*== 1 failed, 1 warnings in *", + ] + ) + assert "None" not in result.stdout.str() + stdout = result.stdout.str() + assert stdout.count("warning_from_test") == 1 + assert stdout.count("=== warnings summary ") == 1 @pytest.mark.parametrize(