diff --git a/AUTHORS b/AUTHORS index f5ba603c2..684063778 100644 --- a/AUTHORS +++ b/AUTHORS @@ -159,6 +159,7 @@ Michael Droettboom Michael Seifert Michal Wajszczuk Mihai Capotă +Mike Hoyle (hoylemd) Mike Lundy Miro Hrončok Nathaniel Waisbrot 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/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/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 ab86fb55f..a3b7d4d5e 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 @@ -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 cb6368a64..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 @@ -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`: @@ -351,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 @@ -363,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 @@ -462,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:: @@ -524,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 @@ -538,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 @@ -589,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 @@ -610,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/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..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 @@ -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 @@ -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 @@ -821,30 +821,30 @@ 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:: $ 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/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..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 @@ -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/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..182aae4fa 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 diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 527a7263a..bee3d6102 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(): @@ -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 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/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..22ce578fc 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): @@ -108,6 +110,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 +125,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 +135,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/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 4eb169575..1897f523b 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 @@ -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 ) @@ -815,7 +815,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'" % ( diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index bd1899466..169b8ddfc 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/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/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/terminal.py b/src/_pytest/terminal.py index f19325088..6f3893653 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,21 @@ 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) + if not warnings: + return + 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 @@ -786,8 +798,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 +819,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): diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 6287c1705..860c2d4af 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 @@ -26,7 +27,14 @@ class TempPathFactory(object): The base directory can be configured using the ``--basetemp`` option.""" - _given_basetemp = attr.ib() + _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 (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))) + ) + ) _trace = attr.ib() _basetemp = attr.ib(default=None) @@ -53,7 +61,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/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/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..130b196ac 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -33,6 +33,23 @@ class TestRaises(object): except pytest.raises.Exception: pass + def test_raises_repr_inflight(self): + """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 + print(str(excinfo)) + print(repr(excinfo)) + import pprint + + pprint.pprint(excinfo) + raise E() + def test_raises_as_contextmanager(self, testdir): testdir.makepyfile( """ @@ -43,7 +60,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_cacheprovider.py b/testing/test_cacheprovider.py index 2b8ca2e18..30fe23aeb 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -899,5 +899,29 @@ 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(u"custom") + cache.set("something", "else") + assert gitignore_path.read_text(encoding="UTF-8") == "custom" + + +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 os.path.isdir("v") # cache contents + assert not os.path.exists(".gitignore") + assert not os.path.exists("README.md") 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 80979d7ee..8bf715995 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -190,7 +190,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..86ec1cd07 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" @@ -1074,11 +1074,54 @@ 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() + 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( diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 38b0672b7..6040d9444 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().resolve() == (tmp_path / "hello").resolve() + class TestConfigTmpdir(object): def test_getbasetemp_custom_removes_old(self, testdir): 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") 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(