diff --git a/AUTHORS b/AUTHORS index c8d770aec..50c2d478a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -63,6 +63,7 @@ Christian Tismer Christoph Buelter Christopher Dignam Christopher Gilling +Claudio Madotto CrazyMerlyn Cyrus Maden Damian Skrzypczak diff --git a/README.rst b/README.rst index 482dde6f5..53e643a5b 100644 --- a/README.rst +++ b/README.rst @@ -137,7 +137,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of Security ^^^^^^^^ -pytest has never been associated with a security vunerability, but in any case, to report a +pytest has never been associated with a security vulnerability, but in any case, to report a security vulnerability please use the `Tidelift security contact `_. Tidelift will coordinate the fix and disclosure. diff --git a/changelog/6290.bugfix.rst b/changelog/6290.bugfix.rst deleted file mode 100644 index f6f1560d4..000000000 --- a/changelog/6290.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -The supporting files in the ``.pytest_cache`` directory are kept with ``--cache-clear``, which only clears cached values now. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 7d8c4c5ac..e44ab212e 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-5.3.2 release-5.3.1 release-5.3.0 release-5.2.4 diff --git a/doc/en/announce/release-5.3.2.rst b/doc/en/announce/release-5.3.2.rst new file mode 100644 index 000000000..dbd657da3 --- /dev/null +++ b/doc/en/announce/release-5.3.2.rst @@ -0,0 +1,26 @@ +pytest-5.3.2 +======================================= + +pytest 5.3.2 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 +* Claudio Madotto +* Daniel Hahler +* Jared Vasquez +* Michael Rose +* Ran Benita +* Ronny Pfannschmidt +* Zac Hatfield-Dodds + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 1c9140f39..46fa240e0 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -24,13 +24,32 @@ with advance notice in the **Deprecations** section of releases. .. include:: _changelog_towncrier_draft.rst - - - - - .. towncrier release notes start +pytest 5.3.2 (2019-12-13) +========================= + +Improvements +------------ + +- `#4639 `_: Revert "A warning is now issued when assertions are made for ``None``". + + The warning proved to be less useful than initially expected and had quite a + few false positive cases. + + + +Bug Fixes +--------- + +- `#5430 `_: junitxml: Logs for failed test are now passed to junit report in case the test fails during call phase. + + +- `#6290 `_: The supporting files in the ``.pytest_cache`` directory are kept with ``--cache-clear``, which only clears cached values now. + + +- `#6301 `_: Fix assertion rewriting for egg-based distributions and ``editable`` installs (``pip install --editable``). + pytest 5.3.1 (2019-11-25) ========================= @@ -796,6 +815,20 @@ Improved Documentation - `#5416 `_: Fix PytestUnknownMarkWarning in run/skip example. +pytest 4.6.7 (2019-12-05) +========================= + +Bug Fixes +--------- + +- `#5477 `_: The XML file produced by ``--junitxml`` now correctly contain a ```` root element. + + +- `#6044 `_: Properly ignore ``FileNotFoundError`` (``OSError.errno == NOENT`` in Python 2) exceptions when trying to remove old temporary directories, + for instance when multiple processes try to remove the same directory (common with ``pytest-xdist`` + for example). + + pytest 4.6.6 (2019-10-11) ========================= diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 7230f2b00..15593b28a 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -475,10 +475,10 @@ Running it results in some skips if we don't have all the python interpreters in .. code-block:: pytest . $ pytest -rs -q multipython.py - ssssssssssssssssssssssss... [100%] + ssssssssssss...ssssssssssss [100%] ========================= short test summary info ========================== SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found - SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.6' not found + SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.7' not found 3 passed, 24 skipped in 0.12s Indirect parametrization of optional implementations/imports @@ -604,13 +604,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker: platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR - collecting ... collected 18 items / 15 deselected / 3 selected + collecting ... collected 17 items / 14 deselected / 3 selected test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%] test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%] test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%] - =============== 2 passed, 15 deselected, 1 xfailed in 0.12s ================ + =============== 2 passed, 14 deselected, 1 xfailed in 0.12s ================ As the result: diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index eb978c5ea..1c06782f6 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: items = [1, 2, 3] print("items is {!r}".format(items)) > a, b = items.pop() - E TypeError: cannot unpack non-iterable int object + E TypeError: 'int' object is not iterable failure_demo.py:181: TypeError --------------------------- Captured stdout call --------------------------- @@ -516,7 +516,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: def test_z2_type_error(self): items = 3 > a, b = items - E TypeError: cannot unpack non-iterable int object + E TypeError: 'int' object is not iterable failure_demo.py:222: TypeError ______________________ TestMoreErrors.test_startswith ______________________ diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index c1e13e3b1..05ccbc9b2 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -442,8 +442,8 @@ Now we can profile which test functions execute the slowest: ========================= slowest 3 test durations ========================= 0.30s call test_some_are_slow.py::test_funcslow2 - 0.21s call test_some_are_slow.py::test_funcslow1 - 0.11s call test_some_are_slow.py::test_funcfast + 0.20s call test_some_are_slow.py::test_funcslow1 + 0.10s call test_some_are_slow.py::test_funcfast ============================ 3 passed in 0.12s ============================= incremental testing - test steps diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 97347f126..59197d0d7 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py + This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest/__init__.py .. _`simpletest`: diff --git a/doc/en/index.rst b/doc/en/index.rst index b0abe7446..9c2f08f3a 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -112,7 +112,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of Security ^^^^^^^^ -pytest has never been associated with a security vunerability, but in any case, to report a +pytest has never been associated with a security vulnerability, but in any case, to report a security vulnerability please use the `Tidelift security contact `_. Tidelift will coordinate the fix and disclosure. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 54827a32c..eba0906b5 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -626,16 +626,67 @@ notset = Notset() def _iter_rewritable_modules(package_files): + """ + Given an iterable of file names in a source distribution, return the "names" that should + be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should + be added as "pytest_mock" in the assertion rewrite mechanism. + + This function has to deal with dist-info based distributions and egg based distributions + (which are still very much in use for "editable" installs). + + Here are the file names as seen in a dist-info based distribution: + + pytest_mock/__init__.py + pytest_mock/_version.py + pytest_mock/plugin.py + pytest_mock.egg-info/PKG-INFO + + Here are the file names as seen in an egg based distribution: + + src/pytest_mock/__init__.py + src/pytest_mock/_version.py + src/pytest_mock/plugin.py + src/pytest_mock.egg-info/PKG-INFO + LICENSE + setup.py + + We have to take in account those two distribution flavors in order to determine which + names should be considered for assertion rewriting. + + More information: + https://github.com/pytest-dev/pytest-mock/issues/167 + """ + package_files = list(package_files) + seen_some = False for fn in package_files: is_simple_module = "/" not in fn and fn.endswith(".py") is_package = fn.count("/") == 1 and fn.endswith("__init__.py") if is_simple_module: module_name, _ = os.path.splitext(fn) - yield module_name + # we ignore "setup.py" at the root of the distribution + if module_name != "setup": + seen_some = True + yield module_name elif is_package: package_name = os.path.dirname(fn) + seen_some = True yield package_name + if not seen_some: + # at this point we did not find any packages or modules suitable for assertion + # rewriting, so we try again by stripping the first path component (to account for + # "src" based source trees for example) + # this approach lets us have the common case continue to be fast, as egg-distributions + # are rarer + new_package_files = [] + for fn in package_files: + parts = fn.split("/") + new_fn = "/".join(parts[1:]) + if new_fn: + new_package_files.append(new_fn) + if new_package_files: + yield from _iter_rewritable_modules(new_package_files) + class Config: """ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 1fdc37c04..69edaf4be 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -43,5 +43,6 @@ NODE_USE_FROM_PARENT = UnformattedWarning( JUNIT_XML_DEFAULT_FAMILY = PytestDeprecationWarning( "The 'junit_family' default value will change to 'xunit2' in pytest 6.0.\n" - "Add 'junit_family=legacy' to your pytest.ini file to silence this warning and make your suite compatible." + "Add 'junit_family=xunit1' to your pytest.ini file to keep the current format " + "in future versions of pytest and silence this warning." ) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 9cf22705e..206e44d96 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -591,6 +591,8 @@ class LogXML: if report.when == "call": reporter.append_failure(report) self.open_reports.append(report) + if not self.log_passing_tests: + reporter.write_captured_output(report) else: reporter.append_error(report) elif report.skipped: diff --git a/testing/test_config.py b/testing/test_config.py index f146b52a4..9735fc176 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -422,15 +422,21 @@ class TestConfigAPI: @pytest.mark.parametrize( "names, expected", [ + # dist-info based distributions root are files as will be put in PYTHONPATH (["bar.py"], ["bar"]), - (["foo", "bar.py"], []), - (["foo", "bar.pyc"], []), - (["foo", "__init__.py"], ["foo"]), - (["foo", "bar", "__init__.py"], []), + (["foo/bar.py"], ["bar"]), + (["foo/bar.pyc"], []), + (["foo/__init__.py"], ["foo"]), + (["bar/__init__.py", "xz.py"], ["bar", "xz"]), + (["setup.py"], []), + # egg based distributions root contain the files from the dist root + (["src/bar/__init__.py"], ["bar"]), + (["src/bar/__init__.py", "setup.py"], ["bar"]), + (["source/python/bar/__init__.py", "setup.py"], ["bar"]), ], ) def test_iter_rewritable_modules(self, names, expected): - assert list(_iter_rewritable_modules(["/".join(names)])) == expected + assert list(_iter_rewritable_modules(names)) == expected class TestConfigFromdictargs: diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 4c2f22a3d..0132db59d 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1477,3 +1477,45 @@ def test_logging_passing_tests_disabled_does_not_log_test_output( node = dom.find_first_by_tag("testcase") assert len(node.find_by_tag("system-err")) == 0 assert len(node.find_by_tag("system-out")) == 0 + + +@parametrize_families +@pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"]) +def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( + testdir, junit_logging, run_and_parse, xunit_family +): + testdir.makeini( + """ + [pytest] + junit_log_passing_tests=False + junit_family={family} + """.format( + family=xunit_family + ) + ) + testdir.makepyfile( + """ + import pytest + import logging + import sys + + def test_func(): + logging.warning('hello') + assert 0 + """ + ) + result, dom = run_and_parse( + "-o", "junit_logging=%s" % junit_logging, family=xunit_family + ) + assert result.ret == 1 + node = dom.find_first_by_tag("testcase") + if junit_logging == "system-out": + assert len(node.find_by_tag("system-err")) == 0 + assert len(node.find_by_tag("system-out")) == 1 + elif junit_logging == "system-err": + assert len(node.find_by_tag("system-err")) == 1 + assert len(node.find_by_tag("system-out")) == 0 + else: + assert junit_logging == "no" + assert len(node.find_by_tag("system-err")) == 0 + assert len(node.find_by_tag("system-out")) == 0