Hello community, here is the log from the commit of package python-pytest4 for openSUSE:Factory checked in at 2019-08-27 15:20:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest4 (Old) and /work/SRC/openSUSE:Factory/.python-pytest4.new.7948 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest4" Tue Aug 27 15:20:38 2019 rev:7 rq:724497 version:4.6.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest4/python-pytest4.changes 2019-07-30 13:02:03.134430645 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest4.new.7948/python-pytest4.changes 2019-08-27 15:20:39.908862643 +0200 @@ -1,0 +2,11 @@ +Mon Aug 19 08:18:32 UTC 2019 - Tomáš Chvátal <[email protected]> + +- Update to 4.6.5: + * #4344: Fix RuntimeError/StopIteration when trying to collect package with "init.py" only. + * #5478: Fix encode error when using unicode strings in exceptions with pytest.raises. + * #5524: Fix issue where tmp_path and tmpdir would not remove directories containing files marked as read-only, + which could lead to pytest crashing when executed a second time with the --basetemp option. + * #5547: --step-wise now handles xfail(strict=True) markers properly. + * #5650: Improved output when parsing an ini configuration file fails. + +------------------------------------------------------------------- Old: ---- pytest-4.6.4.tar.gz New: ---- pytest-4.6.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest4.spec ++++++ --- /var/tmp/diff_new_pack.onDhOZ/_old 2019-08-27 15:20:40.476862356 +0200 +++ /var/tmp/diff_new_pack.onDhOZ/_new 2019-08-27 15:20:40.480862354 +0200 @@ -26,7 +26,7 @@ %bcond_with test %endif Name: python-pytest4%{psuffix} -Version: 4.6.4 +Version: 4.6.5 Release: 0 Summary: Python testing tool with autodiscovery and detailed asserts License: MIT ++++++ pytest-4.6.4.tar.gz -> pytest-4.6.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/.travis.yml new/pytest-4.6.5/.travis.yml --- old/pytest-4.6.4/.travis.yml 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/.travis.yml 2019-08-05 20:04:19.000000000 +0200 @@ -13,6 +13,10 @@ global: - PYTEST_ADDOPTS=-vv +# setuptools-scm needs all tags in order to obtain a proper version +git: + depth: false + install: - python -m pip install --upgrade --pre tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/CHANGELOG.rst new/pytest-4.6.5/CHANGELOG.rst --- old/pytest-4.6.4/CHANGELOG.rst 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/CHANGELOG.rst 2019-08-05 20:04:19.000000000 +0200 @@ -18,6 +18,28 @@ .. towncrier release notes start +pytest 4.6.5 (2019-08-05) +========================= + +Bug Fixes +--------- + +- `#4344 <https://github.com/pytest-dev/pytest/issues/4344>`_: Fix RuntimeError/StopIteration when trying to collect package with "__init__.py" only. + + +- `#5478 <https://github.com/pytest-dev/pytest/issues/5478>`_: Fix encode error when using unicode strings in exceptions with ``pytest.raises``. + + +- `#5524 <https://github.com/pytest-dev/pytest/issues/5524>`_: Fix issue where ``tmp_path`` and ``tmpdir`` would not remove directories containing files marked as read-only, + which could lead to pytest crashing when executed a second time with the ``--basetemp`` option. + + +- `#5547 <https://github.com/pytest-dev/pytest/issues/5547>`_: ``--step-wise`` now handles ``xfail(strict=True)`` markers properly. + + +- `#5650 <https://github.com/pytest-dev/pytest/issues/5650>`_: Improved output when parsing an ini configuration file fails. + + pytest 4.6.4 (2019-06-28) ========================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/PKG-INFO new/pytest-4.6.5/PKG-INFO --- old/pytest-4.6.4/PKG-INFO 2019-06-29 04:00:24.000000000 +0200 +++ new/pytest-4.6.5/PKG-INFO 2019-08-05 20:04:36.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest -Version: 4.6.4 +Version: 4.6.5 Summary: pytest: simple powerful testing with Python Home-page: https://docs.pytest.org/en/latest/ Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/doc/en/announce/index.rst new/pytest-4.6.5/doc/en/announce/index.rst --- old/pytest-4.6.4/doc/en/announce/index.rst 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/doc/en/announce/index.rst 2019-08-05 20:04:19.000000000 +0200 @@ -6,6 +6,7 @@ :maxdepth: 2 + release-4.6.5 release-4.6.4 release-4.6.3 release-4.6.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/doc/en/announce/release-4.6.5.rst new/pytest-4.6.5/doc/en/announce/release-4.6.5.rst --- old/pytest-4.6.4/doc/en/announce/release-4.6.5.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-4.6.5/doc/en/announce/release-4.6.5.rst 2019-08-05 20:04:19.000000000 +0200 @@ -0,0 +1,21 @@ +pytest-4.6.5 +======================================= + +pytest 4.6.5 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 +* Thomas Grainger + + +Happy testing, +The pytest Development Team diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/doc/en/example/parametrize.rst new/pytest-4.6.5/doc/en/example/parametrize.rst --- old/pytest-4.6.4/doc/en/example/parametrize.rst 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/doc/en/example/parametrize.rst 2019-08-05 20:04:19.000000000 +0200 @@ -434,10 +434,11 @@ .. code-block:: pytest . $ pytest -rs -q multipython.py - ...sss...sssssssss...sss... [100%] + ...ssssssssssssssssssssssss [100%] ========================= short test summary info ========================== - SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.4' not found - 12 passed, 15 skipped in 0.12 seconds + SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.4' not found + SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.5' not found + 3 passed, 24 skipped in 0.12 seconds Indirect parametrization of optional implementations/imports -------------------------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/doc/en/example/simple.rst new/pytest-4.6.5/doc/en/example/simple.rst --- old/pytest-4.6.4/doc/en/example/simple.rst 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/doc/en/example/simple.rst 2019-08-05 20:04:19.000000000 +0200 @@ -440,7 +440,7 @@ test_some_are_slow.py ... [100%] ========================= slowest 3 test durations ========================= - 0.30s call test_some_are_slow.py::test_funcslow2 + 0.31s call test_some_are_slow.py::test_funcslow2 0.20s call test_some_are_slow.py::test_funcslow1 0.10s call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.12 seconds ========================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/_code/code.py new/pytest-4.6.5/src/_pytest/_code/code.py --- old/pytest-4.6.4/src/_pytest/_code/code.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/_code/code.py 2019-08-05 20:04:19.000000000 +0200 @@ -572,8 +572,13 @@ raised. """ __tracebackhide__ = True - if not re.search(regexp, str(self.value)): - assert 0, "Pattern '{!s}' not found in '{!s}'".format(regexp, self.value) + value = ( + text_type(self.value) if isinstance(regexp, text_type) else str(self.value) + ) + if not re.search(regexp, value): + raise AssertionError( + u"Pattern {!r} not found in {!r}".format(regexp, value) + ) return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/_version.py new/pytest-4.6.5/src/_pytest/_version.py --- old/pytest-4.6.4/src/_pytest/_version.py 2019-06-29 04:00:23.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/_version.py 2019-08-05 20:04:36.000000000 +0200 @@ -1,4 +1,4 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '4.6.4' +version = '4.6.5' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/cacheprovider.py new/pytest-4.6.5/src/_pytest/cacheprovider.py --- old/pytest-4.6.4/src/_pytest/cacheprovider.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/cacheprovider.py 2019-08-05 20:04:19.000000000 +0200 @@ -21,7 +21,7 @@ from .compat import _PY2 as PY2 from .pathlib import Path from .pathlib import resolve_from_str -from .pathlib import rmtree +from .pathlib import rm_rf README_CONTENT = u"""\ # pytest cache directory # @@ -51,7 +51,7 @@ def for_config(cls, config): cachedir = cls.cache_dir_from_config(config) if config.getoption("cacheclear") and cachedir.exists(): - rmtree(cachedir, force=True) + rm_rf(cachedir) cachedir.mkdir() return cls(cachedir, config) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/config/findpaths.py new/pytest-4.6.5/src/_pytest/config/findpaths.py --- old/pytest-4.6.4/src/_pytest/config/findpaths.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/config/findpaths.py 2019-08-05 20:04:19.000000000 +0200 @@ -33,7 +33,11 @@ for inibasename in inibasenames: p = base.join(inibasename) if exists(p): - iniconfig = py.iniconfig.IniConfig(p) + try: + iniconfig = py.iniconfig.IniConfig(p) + except py.iniconfig.ParseError as exc: + raise UsageError(str(exc)) + if ( inibasename == "setup.cfg" and "tool:pytest" in iniconfig.sections diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/main.py new/pytest-4.6.5/src/_pytest/main.py --- old/pytest-4.6.4/src/_pytest/main.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/main.py 2019-08-05 20:04:19.000000000 +0200 @@ -621,7 +621,13 @@ # Module itself, so just use that. If this special case isn't taken, then all # the files in the package will be yielded. if argpath.basename == "__init__.py": - yield next(m[0].collect()) + try: + yield next(m[0].collect()) + except StopIteration: + # The package collects nothing with only an __init__.py + # file in it, which gets ignored by the default + # "python_files" option. + pass return for y in m: yield y diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/pathlib.py new/pytest-4.6.5/src/_pytest/pathlib.py --- old/pytest-4.6.4/src/_pytest/pathlib.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/pathlib.py 2019-08-05 20:04:19.000000000 +0200 @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import absolute_import + import atexit import errno import fnmatch @@ -8,6 +10,8 @@ import shutil import sys import uuid +import warnings +from functools import partial from functools import reduce from os.path import expanduser from os.path import expandvars @@ -19,6 +23,7 @@ from six.moves import map from .compat import PY36 +from _pytest.warning_types import PytestWarning if PY36: from pathlib import Path, PurePath @@ -38,17 +43,57 @@ ensures the given path is an empty directory """ if path.exists(): - rmtree(path, force=True) + rm_rf(path) path.mkdir() -def rmtree(path, force=False): - if force: - # NOTE: ignore_errors might leave dead folders around. - # Python needs a rm -rf as a followup. - shutil.rmtree(str(path), ignore_errors=True) - else: - shutil.rmtree(str(path)) +def on_rm_rf_error(func, path, exc, **kwargs): + """Handles known read-only errors during rmtree.""" + start_path = kwargs["start_path"] + excvalue = exc[1] + + if not isinstance(excvalue, OSError) or excvalue.errno not in ( + errno.EACCES, + errno.EPERM, + ): + warnings.warn( + PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue)) + ) + return + + if func not in (os.rmdir, os.remove, os.unlink): + warnings.warn( + PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue)) + ) + return + + # Chmod + retry. + import stat + + def chmod_rw(p): + mode = os.stat(p).st_mode + os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) + + # For files, we need to recursively go upwards in the directories to + # ensure they all are also writable. + p = Path(path) + if p.is_file(): + for parent in p.parents: + chmod_rw(str(parent)) + # stop when we reach the original path passed to rm_rf + if parent == start_path: + break + chmod_rw(str(path)) + + func(path) + + +def rm_rf(path): + """Remove the path contents recursively, even if some elements + are read-only. + """ + onerror = partial(on_rm_rf_error, start_path=path) + shutil.rmtree(str(path), onerror=onerror) def find_prefixed(root, prefix): @@ -186,7 +231,7 @@ garbage = parent.joinpath("garbage-{}".format(uuid.uuid4())) path.rename(garbage) - rmtree(garbage, force=True) + rm_rf(garbage) except (OSError, EnvironmentError): # known races: # * other process did a cleanup at the same time diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/_pytest/stepwise.py new/pytest-4.6.5/src/_pytest/stepwise.py --- old/pytest-4.6.4/src/_pytest/stepwise.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/src/_pytest/stepwise.py 2019-08-05 20:04:19.000000000 +0200 @@ -72,8 +72,7 @@ config.hook.pytest_deselected(items=already_passed) def pytest_runtest_logreport(self, report): - # Skip this hook if plugin is not active or the test is xfailed. - if not self.active or "xfail" in report.keywords: + if not self.active: return if report.failed: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/pytest.egg-info/PKG-INFO new/pytest-4.6.5/src/pytest.egg-info/PKG-INFO --- old/pytest-4.6.4/src/pytest.egg-info/PKG-INFO 2019-06-29 04:00:23.000000000 +0200 +++ new/pytest-4.6.5/src/pytest.egg-info/PKG-INFO 2019-08-05 20:04:36.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest -Version: 4.6.4 +Version: 4.6.5 Summary: pytest: simple powerful testing with Python Home-page: https://docs.pytest.org/en/latest/ Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/src/pytest.egg-info/SOURCES.txt new/pytest-4.6.5/src/pytest.egg-info/SOURCES.txt --- old/pytest-4.6.4/src/pytest.egg-info/SOURCES.txt 2019-06-29 04:00:24.000000000 +0200 +++ new/pytest-4.6.5/src/pytest.egg-info/SOURCES.txt 2019-08-05 20:04:36.000000000 +0200 @@ -198,6 +198,7 @@ doc/en/announce/release-4.6.2.rst doc/en/announce/release-4.6.3.rst doc/en/announce/release-4.6.4.rst +doc/en/announce/release-4.6.5.rst doc/en/announce/sprint2016.rst doc/en/example/attic.rst doc/en/example/conftest.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/python/raises.py new/pytest-4.6.5/testing/python/raises.py --- old/pytest-4.6.4/testing/python/raises.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/python/raises.py 2019-08-05 20:04:19.000000000 +0200 @@ -4,6 +4,7 @@ import six import pytest +from _pytest.compat import dummy_context_manager from _pytest.outcomes import Failed from _pytest.warning_types import PytestDeprecationWarning @@ -220,7 +221,7 @@ int("asdf") msg = "with base 16" - expr = r"Pattern '{}' not found in 'invalid literal for int\(\) with base 10: 'asdf''".format( + expr = r"Pattern '{}' not found in \"invalid literal for int\(\) with base 10: 'asdf'\"".format( msg ) with pytest.raises(AssertionError, match=expr): @@ -278,3 +279,47 @@ with pytest.raises(CrappyClass()): pass assert "via __class__" in excinfo.value.args[0] + + +class TestUnicodeHandling: + """Test various combinations of bytes and unicode with pytest.raises (#5478) + + https://github.com/pytest-dev/pytest/pull/5479#discussion_r298852433 + """ + + success = dummy_context_manager + py2_only = pytest.mark.skipif( + six.PY3, reason="bytes in raises only supported in Python 2" + ) + + @pytest.mark.parametrize( + "message, match, expectation", + [ + (u"\u2603", u"\u2603", success()), + (u"\u2603", u"\u2603foo", pytest.raises(AssertionError)), + pytest.param(b"hello", b"hello", success(), marks=py2_only), + pytest.param( + b"hello", b"world", pytest.raises(AssertionError), marks=py2_only + ), + pytest.param(u"hello", b"hello", success(), marks=py2_only), + pytest.param( + u"hello", b"world", pytest.raises(AssertionError), marks=py2_only + ), + pytest.param( + u"😊".encode("UTF-8"), + b"world", + pytest.raises(AssertionError), + marks=py2_only, + ), + pytest.param( + u"world", + u"😊".encode("UTF-8"), + pytest.raises(AssertionError), + marks=py2_only, + ), + ], + ) + def test_handling(self, message, match, expectation): + with expectation: + with pytest.raises(RuntimeError, match=match): + raise RuntimeError(message) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/test_collection.py new/pytest-4.6.5/testing/test_collection.py --- old/pytest-4.6.4/testing/test_collection.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/test_collection.py 2019-08-05 20:04:19.000000000 +0200 @@ -1211,6 +1211,18 @@ ) +def test_collect_pkg_init_only(testdir): + subdir = testdir.mkdir("sub") + init = subdir.ensure("__init__.py") + init.write("def test_init(): pass") + + result = testdir.runpytest(str(init)) + result.stdout.fnmatch_lines(["*no tests ran in*"]) + + result = testdir.runpytest("-v", "-o", "python_files=*.py", str(init)) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + + @pytest.mark.skipif( not hasattr(py.path.local, "mksymlinkto"), reason="symlink not available on this platform", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/test_config.py new/pytest-4.6.5/testing/test_config.py --- old/pytest-4.6.4/testing/test_config.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/test_config.py 2019-08-05 20:04:19.000000000 +0200 @@ -131,6 +131,12 @@ config = testdir.parseconfigure(sub) assert config.getini("minversion") == "2.0" + def test_ini_parse_error(self, testdir): + testdir.tmpdir.join("pytest.ini").write("addopts = -x") + result = testdir.runpytest() + assert result.ret != 0 + result.stderr.fnmatch_lines(["ERROR: *pytest.ini:1: no section header defined"]) + @pytest.mark.xfail(reason="probably not needed") def test_confcutdir(self, testdir): sub = testdir.mkdir("sub") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/test_pytester.py new/pytest-4.6.5/testing/test_pytester.py --- old/pytest-4.6.4/testing/test_pytester.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/test_pytester.py 2019-08-05 20:04:19.000000000 +0200 @@ -245,8 +245,8 @@ ): spy_factory = self.spy_factory() monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) - original = dict(sys.modules) testdir.syspathinsert() + original = dict(sys.modules) testdir.makepyfile(import1="# you son of a silly person") testdir.makepyfile(import2="# my hovercraft is full of eels") test_mod = testdir.makepyfile( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/test_stepwise.py new/pytest-4.6.5/testing/test_stepwise.py --- old/pytest-4.6.4/testing/test_stepwise.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/test_stepwise.py 2019-08-05 20:04:19.000000000 +0200 @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +import sys + import pytest @@ -166,3 +168,57 @@ files.reverse() result = broken_testdir.runpytest("-v", "--strict-markers", "--stepwise", *files) result.stdout.fnmatch_lines("*errors during collection*") + + +def test_xfail_handling(testdir): + """Ensure normal xfail is ignored, and strict xfail interrupts the session in sw mode + + (#5547) + """ + contents = """ + import pytest + def test_a(): pass + + @pytest.mark.xfail(strict={strict}) + def test_b(): assert {assert_value} + + def test_c(): pass + def test_d(): pass + """ + testdir.makepyfile(contents.format(assert_value="0", strict="False")) + result = testdir.runpytest("--sw", "-v") + result.stdout.fnmatch_lines( + [ + "*::test_a PASSED *", + "*::test_b XFAIL *", + "*::test_c PASSED *", + "*::test_d PASSED *", + "* 3 passed, 1 xfailed in *", + ] + ) + + testdir.makepyfile(contents.format(assert_value="1", strict="True")) + result = testdir.runpytest("--sw", "-v") + result.stdout.fnmatch_lines( + [ + "*::test_a PASSED *", + "*::test_b FAILED *", + "* Interrupted*", + "* 1 failed, 1 passed in *", + ] + ) + + # because we are writing to the same file, mtime might not be affected enough to + # invalidate the cache, making this next run flaky + if not sys.dont_write_bytecode: + testdir.tmpdir.join("__pycache__").remove() + testdir.makepyfile(contents.format(assert_value="0", strict="True")) + result = testdir.runpytest("--sw", "-v") + result.stdout.fnmatch_lines( + [ + "*::test_b XFAIL *", + "*::test_c PASSED *", + "*::test_d PASSED *", + "* 2 passed, 1 deselected, 1 xfailed in *", + ] + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-4.6.4/testing/test_tmpdir.py new/pytest-4.6.5/testing/test_tmpdir.py --- old/pytest-4.6.4/testing/test_tmpdir.py 2019-06-29 04:00:08.000000000 +0200 +++ new/pytest-4.6.5/testing/test_tmpdir.py 2019-08-05 20:04:19.000000000 +0200 @@ -3,6 +3,9 @@ from __future__ import division from __future__ import print_function +import errno +import os +import stat import sys import attr @@ -317,12 +320,25 @@ p, consider_lock_dead_if_created_before=p.stat().st_mtime + 1 ) - def test_rmtree(self, tmp_path): - from _pytest.pathlib import rmtree + def test_cleanup_ignores_symlink(self, tmp_path): + the_symlink = tmp_path / (self.PREFIX + "current") + attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5")) + self._do_cleanup(tmp_path) + + def test_removal_accepts_lock(self, tmp_path): + folder = pathlib.make_numbered_dir(root=tmp_path, prefix=self.PREFIX) + pathlib.create_cleanup_lock(folder) + pathlib.maybe_delete_a_numbered_dir(folder) + assert folder.is_dir() + + +class TestRmRf: + def test_rm_rf(self, tmp_path): + from _pytest.pathlib import rm_rf adir = tmp_path / "adir" adir.mkdir() - rmtree(adir) + rm_rf(adir) assert not adir.exists() @@ -330,19 +346,70 @@ afile = adir / "afile" afile.write_bytes(b"aa") - rmtree(adir, force=True) + rm_rf(adir) assert not adir.exists() - def test_cleanup_ignores_symlink(self, tmp_path): - the_symlink = tmp_path / (self.PREFIX + "current") - attempt_symlink_to(the_symlink, tmp_path / (self.PREFIX + "5")) - self._do_cleanup(tmp_path) + def test_rm_rf_with_read_only_file(self, tmp_path): + """Ensure rm_rf can remove directories with read-only files in them (#5524)""" + from _pytest.pathlib import rm_rf - def test_removal_accepts_lock(self, tmp_path): - folder = pathlib.make_numbered_dir(root=tmp_path, prefix=self.PREFIX) - pathlib.create_cleanup_lock(folder) - pathlib.maybe_delete_a_numbered_dir(folder) - assert folder.is_dir() + fn = tmp_path / "dir/foo.txt" + fn.parent.mkdir() + + fn.touch() + + self.chmod_r(fn) + + rm_rf(fn.parent) + + assert not fn.parent.is_dir() + + def chmod_r(self, path): + mode = os.stat(str(path)).st_mode + os.chmod(str(path), mode & ~stat.S_IWRITE) + + def test_rm_rf_with_read_only_directory(self, tmp_path): + """Ensure rm_rf can remove read-only directories (#5524)""" + from _pytest.pathlib import rm_rf + + adir = tmp_path / "dir" + adir.mkdir() + + (adir / "foo.txt").touch() + self.chmod_r(adir) + + rm_rf(adir) + + assert not adir.is_dir() + + def test_on_rm_rf_error(self, tmp_path): + from _pytest.pathlib import on_rm_rf_error + + adir = tmp_path / "dir" + adir.mkdir() + + fn = adir / "foo.txt" + fn.touch() + self.chmod_r(fn) + + # unknown exception + with pytest.warns(pytest.PytestWarning): + exc_info = (None, RuntimeError(), None) + on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path) + assert fn.is_file() + + permission_error = OSError() + permission_error.errno = errno.EACCES + + # unknown function + with pytest.warns(pytest.PytestWarning): + exc_info = (None, permission_error, None) + on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path) + assert fn.is_file() + + exc_info = (None, permission_error, None) + on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path) + assert not fn.is_file() def attempt_symlink_to(path, to_path): @@ -358,3 +425,24 @@ def test_tmpdir_equals_tmp_path(tmpdir, tmp_path): assert Path(tmpdir) == tmp_path + + +def test_basetemp_with_read_only_files(testdir): + """Integration test for #5524""" + testdir.makepyfile( + """ + import os + import stat + + def test(tmp_path): + fn = tmp_path / 'foo.txt' + fn.write_text(u'hello') + mode = os.stat(str(fn)).st_mode + os.chmod(str(fn), mode & ~stat.S_IREAD) + """ + ) + result = testdir.runpytest("--basetemp=tmp") + assert result.ret == 0 + # running a second time and ensure we don't crash + result = testdir.runpytest("--basetemp=tmp") + assert result.ret == 0
