Hello community, here is the log from the commit of package python-pytest for openSUSE:Factory checked in at 2020-06-11 10:01:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest (Old) and /work/SRC/openSUSE:Factory/.python-pytest.new.3606 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest" Thu Jun 11 10:01:16 2020 rev:56 rq:813080 version:5.4.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest/python-pytest.changes 2020-05-28 09:04:54.675275669 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest.new.3606/python-pytest.changes 2020-06-11 10:01:41.134578424 +0200 @@ -1,0 +2,18 @@ +Tue Jun 9 07:43:57 UTC 2020 - Steve Kowalik <steven.kowa...@suse.com> + +- Rebase and re-add tidy-up-embeddedfile.patch, it was only partially merged + upstream. + +------------------------------------------------------------------- +Fri Jun 5 09:03:56 UTC 2020 - Ondřej Súkup <mimi...@gmail.com> + +- update to 5.4.3 + * Paths appearing in error messages are now correct in case the current working + directory has changed since the start of the session. + * Support deleting paths longer than 260 characters on windows created inside tmpdir. + * Prevent pytest from printing ConftestImportFailure traceback to stdout. + * Prevent hiding the underlying exception when ConfTestImportFailure is raised. + * Fix regression where running with --pdb would call the tearDown methods + of unittest.TestCase subclasses for skipped tests. + +------------------------------------------------------------------- Old: ---- pytest-5.4.2.tar.gz New: ---- pytest-5.4.3.tar.gz tidy-up-embeddedfile.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest.spec ++++++ --- /var/tmp/diff_new_pack.GPZRlg/_old 2020-06-11 10:01:44.278588550 +0200 +++ /var/tmp/diff_new_pack.GPZRlg/_new 2020-06-11 10:01:44.282588563 +0200 @@ -27,12 +27,13 @@ %endif %define skip_python2 1 Name: python-pytest%{psuffix} -Version: 5.4.2 +Version: 5.4.3 Release: 0 Summary: Python testing tool with autodiscovery and detailed asserts License: MIT URL: https://github.com/pytest-dev/pytest Source: https://files.pythonhosted.org/packages/source/p/pytest/pytest-%{version}.tar.gz +Patch0: tidy-up-embeddedfile.patch BuildRequires: %{python_module setuptools >= 40.0} BuildRequires: %{python_module setuptools_scm} BuildRequires: fdupes @@ -87,6 +88,7 @@ %prep %setup -q -n pytest-%{version} +%autopatch -p1 %build %python_build ++++++ pytest-5.4.2.tar.gz -> pytest-5.4.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/AUTHORS new/pytest-5.4.3/AUTHORS --- old/pytest-5.4.2/AUTHORS 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/AUTHORS 2020-06-02 18:43:25.000000000 +0200 @@ -267,6 +267,7 @@ Tom Viner Tomáš Gavenčiak Tomer Keren +Tor Colvin Trevor Bekolay Tyler Goodlet Tzu-ping Chung diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/PKG-INFO new/pytest-5.4.3/PKG-INFO --- old/pytest-5.4.2/PKG-INFO 2020-05-08 13:58:23.000000000 +0200 +++ new/pytest-5.4.3/PKG-INFO 2020-06-02 18:43:38.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest -Version: 5.4.2 +Version: 5.4.3 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-5.4.2/codecov.yml new/pytest-5.4.3/codecov.yml --- old/pytest-5.4.2/codecov.yml 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/codecov.yml 2020-06-02 18:43:25.000000000 +0200 @@ -1 +1,6 @@ -comment: off +# reference: https://docs.codecov.io/docs/codecovyml-reference +coverage: + status: + patch: true + project: false +comment: false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/doc/en/announce/index.rst new/pytest-5.4.3/doc/en/announce/index.rst --- old/pytest-5.4.2/doc/en/announce/index.rst 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/doc/en/announce/index.rst 2020-06-02 18:43:25.000000000 +0200 @@ -6,6 +6,7 @@ :maxdepth: 2 + release-5.4.3 release-5.4.2 release-5.4.1 release-5.4.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/doc/en/announce/release-5.4.3.rst new/pytest-5.4.3/doc/en/announce/release-5.4.3.rst --- old/pytest-5.4.2/doc/en/announce/release-5.4.3.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/pytest-5.4.3/doc/en/announce/release-5.4.3.rst 2020-06-02 18:43:25.000000000 +0200 @@ -0,0 +1,21 @@ +pytest-5.4.3 +======================================= + +pytest 5.4.3 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 +* Ran Benita +* Tor Colvin + + +Happy testing, +The pytest Development Team diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/doc/en/changelog.rst new/pytest-5.4.3/doc/en/changelog.rst --- old/pytest-5.4.2/doc/en/changelog.rst 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/doc/en/changelog.rst 2020-06-02 18:43:25.000000000 +0200 @@ -28,6 +28,29 @@ .. towncrier release notes start +pytest 5.4.3 (2020-06-02) +========================= + +Bug Fixes +--------- + +- `#6428 <https://github.com/pytest-dev/pytest/issues/6428>`_: Paths appearing in error messages are now correct in case the current working directory has + changed since the start of the session. + + +- `#6755 <https://github.com/pytest-dev/pytest/issues/6755>`_: Support deleting paths longer than 260 characters on windows created inside tmpdir. + + +- `#6956 <https://github.com/pytest-dev/pytest/issues/6956>`_: Prevent pytest from printing ConftestImportFailure traceback to stdout. + + +- `#7150 <https://github.com/pytest-dev/pytest/issues/7150>`_: Prevent hiding the underlying exception when ``ConfTestImportFailure`` is raised. + + +- `#7215 <https://github.com/pytest-dev/pytest/issues/7215>`_: Fix regression where running with ``--pdb`` would call the ``tearDown`` methods of ``unittest.TestCase`` + subclasses for skipped tests. + + pytest 5.4.2 (2020-05-08) ========================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/doc/en/example/parametrize.rst new/pytest-5.4.3/doc/en/example/parametrize.rst --- old/pytest-5.4.2/doc/en/example/parametrize.rst 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/doc/en/example/parametrize.rst 2020-06-02 18:43:25.000000000 +0200 @@ -481,11 +481,10 @@ .. code-block:: pytest . $ pytest -rs -q multipython.py - ssssssssssss...ssssssssssss [100%] + ssssssssssss......sss...... [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.7' not found - 3 passed, 24 skipped in 0.12s + SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.5' not found + 12 passed, 15 skipped in 0.12s Indirect parametrization of optional implementations/imports -------------------------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/_pytest/_version.py new/pytest-5.4.3/src/_pytest/_version.py --- old/pytest-5.4.2/src/_pytest/_version.py 2020-05-08 13:58:23.000000000 +0200 +++ new/pytest-5.4.3/src/_pytest/_version.py 2020-06-02 18:43:38.000000000 +0200 @@ -1,4 +1,4 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '5.4.2' +version = '5.4.3' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/_pytest/debugging.py new/pytest-5.4.3/src/_pytest/debugging.py --- old/pytest-5.4.2/src/_pytest/debugging.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/src/_pytest/debugging.py 2020-06-02 18:43:25.000000000 +0200 @@ -4,6 +4,7 @@ import sys from _pytest import outcomes +from _pytest.config import ConftestImportFailure from _pytest.config import hookimpl from _pytest.config.exceptions import UsageError @@ -338,6 +339,10 @@ # A doctest.UnexpectedException is not useful for post_mortem. # Use the underlying exception instead: return excinfo.value.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + return excinfo.value.excinfo[2] else: return excinfo._excinfo[2] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/_pytest/nodes.py new/pytest-5.4.3/src/_pytest/nodes.py --- old/pytest-5.4.2/src/_pytest/nodes.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/src/_pytest/nodes.py 2020-06-02 18:43:25.000000000 +0200 @@ -19,6 +19,7 @@ from _pytest.compat import cached_property from _pytest.compat import TYPE_CHECKING from _pytest.config import Config +from _pytest.config import ConftestImportFailure from _pytest.config import PytestPluginManager from _pytest.deprecated import NODE_USE_FROM_PARENT from _pytest.fixtures import FixtureDef @@ -28,7 +29,7 @@ from _pytest.mark.structures import MarkDecorator from _pytest.mark.structures import NodeKeywords from _pytest.outcomes import fail -from _pytest.outcomes import Failed +from _pytest.pathlib import Path from _pytest.store import Store if TYPE_CHECKING: @@ -318,8 +319,10 @@ pass def _repr_failure_py( - self, excinfo: ExceptionInfo[Union[Failed, FixtureLookupError]], style=None + self, excinfo: ExceptionInfo[BaseException], style=None, ) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]: + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo(excinfo.value.excinfo) if isinstance(excinfo.value, fail.Exception): if not excinfo.value.pytrace: return str(excinfo.value) @@ -346,9 +349,14 @@ else: truncate_locals = True + # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. + # It is possible for a fixture/test to change the CWD while this code runs, which + # would then result in the user seeing confusing paths in the failure message. + # To fix this, if the CWD changed, always display the full absolute path. + # It will be better to just always display paths relative to invocation_dir, but + # this requires a lot of plumbing (#6428). try: - os.getcwd() - abspath = False + abspath = Path(os.getcwd()) != Path(self.config.invocation_dir) except OSError: abspath = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/_pytest/pathlib.py new/pytest-5.4.3/src/_pytest/pathlib.py --- old/pytest-5.4.2/src/_pytest/pathlib.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/src/_pytest/pathlib.py 2020-06-02 18:43:25.000000000 +0200 @@ -100,10 +100,41 @@ return True +def ensure_extended_length_path(path: Path) -> Path: + """Get the extended-length version of a path (Windows). + + On Windows, by default, the maximum length of a path (MAX_PATH) is 260 + characters, and operations on paths longer than that fail. But it is possible + to overcome this by converting the path to "extended-length" form before + performing the operation: + https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + + On Windows, this function returns the extended-length absolute version of path. + On other platforms it returns path unchanged. + """ + if sys.platform.startswith("win32"): + path = path.resolve() + path = Path(get_extended_length_path_str(str(path))) + return path + + +def get_extended_length_path_str(path: str) -> str: + """Converts to extended length path as a str""" + long_path_prefix = "\\\\?\\" + unc_long_path_prefix = "\\\\?\\UNC\\" + if path.startswith((long_path_prefix, unc_long_path_prefix)): + return path + # UNC + if path.startswith("\\\\"): + return unc_long_path_prefix + path[2:] + return long_path_prefix + path + + def rm_rf(path: Path) -> None: """Remove the path contents recursively, even if some elements are read-only. """ + path = ensure_extended_length_path(path) onerror = partial(on_rm_rf_error, start_path=path) shutil.rmtree(str(path), onerror=onerror) @@ -220,6 +251,7 @@ def maybe_delete_a_numbered_dir(path: Path) -> None: """removes a numbered directory if its lock can be obtained and it does not seem to be in use""" + path = ensure_extended_length_path(path) lock_path = None try: lock_path = create_cleanup_lock(path) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/_pytest/unittest.py new/pytest-5.4.3/src/_pytest/unittest.py --- old/pytest-5.4.2/src/_pytest/unittest.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/src/_pytest/unittest.py 2020-06-02 18:43:25.000000000 +0200 @@ -41,7 +41,7 @@ if not getattr(cls, "__test__", True): return - skipped = getattr(cls, "__unittest_skip__", False) + skipped = _is_skipped(cls) if not skipped: self._inject_setup_teardown_fixtures(cls) self._inject_setup_class_fixture() @@ -89,7 +89,7 @@ @pytest.fixture(scope=scope, autouse=True) def fixture(self, request): - if getattr(self, "__unittest_skip__", None): + if _is_skipped(self): reason = self.__unittest_skip_why__ pytest.skip(reason) if setup is not None: @@ -220,7 +220,7 @@ # arguably we could always postpone tearDown(), but this changes the moment where the # TestCase instance interacts with the results object, so better to only do it # when absolutely needed - if self.config.getoption("usepdb"): + if self.config.getoption("usepdb") and not _is_skipped(self.obj): self._explicit_tearDown = self._testcase.tearDown setattr(self._testcase, "tearDown", lambda *args: None) @@ -301,3 +301,8 @@ classImplements(TestCaseFunction, IReporter) done.append(1) + + +def _is_skipped(obj) -> bool: + """Return True if the given object has been marked with @unittest.skip""" + return bool(getattr(obj, "__unittest_skip__", False)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/src/pytest.egg-info/PKG-INFO new/pytest-5.4.3/src/pytest.egg-info/PKG-INFO --- old/pytest-5.4.2/src/pytest.egg-info/PKG-INFO 2020-05-08 13:58:23.000000000 +0200 +++ new/pytest-5.4.3/src/pytest.egg-info/PKG-INFO 2020-06-02 18:43:38.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest -Version: 5.4.2 +Version: 5.4.3 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-5.4.2/src/pytest.egg-info/SOURCES.txt new/pytest-5.4.3/src/pytest.egg-info/SOURCES.txt --- old/pytest-5.4.2/src/pytest.egg-info/SOURCES.txt 2020-05-08 13:58:23.000000000 +0200 +++ new/pytest-5.4.3/src/pytest.egg-info/SOURCES.txt 2020-06-02 18:43:38.000000000 +0200 @@ -228,6 +228,7 @@ doc/en/announce/release-5.4.0.rst doc/en/announce/release-5.4.1.rst doc/en/announce/release-5.4.2.rst +doc/en/announce/release-5.4.3.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-5.4.2/testing/python/collect.py new/pytest-5.4.3/testing/python/collect.py --- old/pytest-5.4.2/testing/python/collect.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/python/collect.py 2020-06-02 18:43:25.000000000 +0200 @@ -1225,7 +1225,7 @@ result.stdout.fnmatch_lines(["*ERROR collecting*", "*SyntaxError*", "*1 error in*"]) -def test_collecterror_with_fulltrace(testdir): +def test_collect_error_with_fulltrace(testdir): testdir.makepyfile("assert 0") result = testdir.runpytest("--fulltrace") result.stdout.fnmatch_lines( @@ -1233,15 +1233,12 @@ "collected 0 items / 1 error", "", "*= ERRORS =*", - "*_ ERROR collecting test_collecterror_with_fulltrace.py _*", - "", - "*/_pytest/python.py:*: ", - "_ _ _ _ _ _ _ _ *", + "*_ ERROR collecting test_collect_error_with_fulltrace.py _*", "", "> assert 0", "E assert 0", "", - "test_collecterror_with_fulltrace.py:1: AssertionError", + "test_collect_error_with_fulltrace.py:1: AssertionError", "*! Interrupted: 1 error during collection !*", ] ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/testing/test_debugging.py new/pytest-5.4.3/testing/test_debugging.py --- old/pytest-5.4.2/testing/test_debugging.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/test_debugging.py 2020-06-02 18:43:25.000000000 +0200 @@ -342,6 +342,15 @@ child.sendeof() self.flush(child) + def test_pdb_prevent_ConftestImportFailure_hiding_exception(self, testdir): + testdir.makepyfile("def test_func(): pass") + sub_dir = testdir.tmpdir.join("ns").ensure_dir() + sub_dir.join("conftest").new(ext=".py").write("import unknown") + sub_dir.join("test_file").new(ext=".py").write("def test_func(): pass") + + result = testdir.runpytest_subprocess("--pdb", ".") + result.stdout.fnmatch_lines(["-> import unknown"]) + def test_pdb_interaction_capturing_simple(self, testdir): p1 = testdir.makepyfile( """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/testing/test_nodes.py new/pytest-5.4.3/testing/test_nodes.py --- old/pytest-5.4.2/testing/test_nodes.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/test_nodes.py 2020-06-02 18:43:25.000000000 +0200 @@ -58,3 +58,30 @@ outside = py.path.local("/outside") assert nodes._check_initialpaths_for_relpath(FakeSession, outside) is None + + +def test_failure_with_changed_cwd(testdir): + """ + Test failure lines should use absolute paths if cwd has changed since + invocation, so the path is correct (#6428). + """ + p = testdir.makepyfile( + """ + import os + import pytest + + @pytest.fixture + def private_dir(): + out_dir = 'ddd' + os.mkdir(out_dir) + old_dir = os.getcwd() + os.chdir(out_dir) + yield out_dir + os.chdir(old_dir) + + def test_show_wrong_path(private_dir): + assert False + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines([str(p) + ":*: AssertionError", "*1 failed in *"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/testing/test_pathlib.py new/pytest-5.4.3/testing/test_pathlib.py --- old/pytest-5.4.2/testing/test_pathlib.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/test_pathlib.py 2020-06-02 18:43:25.000000000 +0200 @@ -5,6 +5,7 @@ import pytest from _pytest.pathlib import fnmatch_ex +from _pytest.pathlib import get_extended_length_path_str from _pytest.pathlib import get_lock_path from _pytest.pathlib import maybe_delete_a_numbered_dir from _pytest.pathlib import Path @@ -89,3 +90,26 @@ lock_path = get_lock_path(path) maybe_delete_a_numbered_dir(path) assert not lock_path.is_file() + + +def test_long_path_during_cleanup(tmp_path): + """Ensure that deleting long path works (particularly on Windows (#6775)).""" + path = (tmp_path / ("a" * 250)).resolve() + if sys.platform == "win32": + # make sure that the full path is > 260 characters without any + # component being over 260 characters + assert len(str(path)) > 260 + extended_path = "\\\\?\\" + str(path) + else: + extended_path = str(path) + os.mkdir(extended_path) + assert os.path.isdir(extended_path) + maybe_delete_a_numbered_dir(path) + assert not os.path.isdir(extended_path) + + +def test_get_extended_length_path_str(): + assert get_extended_length_path_str(r"c:\foo") == r"\\?\c:\foo" + assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo" + assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo" + assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/testing/test_reports.py new/pytest-5.4.3/testing/test_reports.py --- old/pytest-5.4.2/testing/test_reports.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/test_reports.py 2020-06-02 18:43:25.000000000 +0200 @@ -395,6 +395,14 @@ # for same reasons as previous test, ensure we don't blow up here loaded_report.longrepr.toterminal(tw_mock) + def test_report_prevent_ConftestImportFailure_hiding_exception(self, testdir): + sub_dir = testdir.tmpdir.join("ns").ensure_dir() + sub_dir.join("conftest").new(ext=".py").write("import unknown") + + result = testdir.runpytest_subprocess(".") + result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"]) + result.stdout.no_fnmatch_line("ERROR - *ConftestImportFailure*") + class TestHooks: """Test that the hooks are working correctly for plugins""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-5.4.2/testing/test_unittest.py new/pytest-5.4.3/testing/test_unittest.py --- old/pytest-5.4.2/testing/test_unittest.py 2020-05-08 13:58:14.000000000 +0200 +++ new/pytest-5.4.3/testing/test_unittest.py 2020-06-02 18:43:25.000000000 +0200 @@ -1193,6 +1193,40 @@ ] +@pytest.mark.parametrize("mark", ["@unittest.skip", "@pytest.mark.skip"]) +def test_pdb_teardown_skipped(testdir, monkeypatch, mark): + """ + With --pdb, setUp and tearDown should not be called for skipped tests. + """ + tracked = [] + monkeypatch.setattr(pytest, "test_pdb_teardown_skipped", tracked, raising=False) + + testdir.makepyfile( + """ + import unittest + import pytest + + class MyTestCase(unittest.TestCase): + + def setUp(self): + pytest.test_pdb_teardown_skipped.append("setUp:" + self.id()) + + def tearDown(self): + pytest.test_pdb_teardown_skipped.append("tearDown:" + self.id()) + + {mark}("skipped for reasons") + def test_1(self): + pass + + """.format( + mark=mark + ) + ) + result = testdir.runpytest_inprocess("--pdb") + result.stdout.fnmatch_lines("* 1 skipped in *") + assert tracked == [] + + def test_async_support(testdir): pytest.importorskip("unittest.async_case") ++++++ tidy-up-embeddedfile.patch ++++++ >From 29e4cb5d45f44379aba948c2cd791b3b97210e31 Mon Sep 17 00:00:00 2001 From: Ran Benita <r...@unusedvar.com> Date: Sat, 7 Mar 2020 18:38:22 +0200 Subject: [PATCH] Remove safe_text_dupfile() and simplify EncodedFile I tried to understand what the `safe_text_dupfile()` function and `EncodedFile` class do. Outside tests, `EncodedFile` is only used by `safe_text_dupfile`, and `safe_text_dupfile` is only used by `FDCaptureBinary.__init__()`. I then started to eliminate always-true conditions based on the single call site, and in the end nothing was left except of a couple workarounds that are still needed. --- src/_pytest/capture.py | 66 ++++++++++-------------------------- testing/test_capture.py | 75 ++++++----------------------------------- 2 files changed, 28 insertions(+), 113 deletions(-) Index: pytest-5.4.3/src/_pytest/capture.py =================================================================== --- pytest-5.4.3.orig/src/_pytest/capture.py +++ pytest-5.4.3/src/_pytest/capture.py @@ -390,54 +390,21 @@ class CaptureFixture: yield -def safe_text_dupfile(f, mode, default_encoding="UTF8"): - """ return an open text file object that's a duplicate of f on the - FD-level if possible. - """ - encoding = getattr(f, "encoding", None) - try: - fd = f.fileno() - except Exception: - if "b" not in getattr(f, "mode", "") and hasattr(f, "encoding"): - # we seem to have a text stream, let's just use it - return f - else: - newfd = os.dup(fd) - if "b" not in mode: - mode += "b" - f = os.fdopen(newfd, mode, 0) # no buffering - return EncodedFile(f, encoding or default_encoding) - - -class EncodedFile: - errors = "strict" # possibly needed by py3 code (issue555) - - def __init__(self, buffer: BinaryIO, encoding: str) -> None: - self.buffer = buffer - self.encoding = encoding - - def write(self, s: str) -> int: - if not isinstance(s, str): - raise TypeError( - "write() argument must be str, not {}".format(type(s).__name__) - ) - return self.buffer.write(s.encode(self.encoding, "replace")) - - def writelines(self, lines: Iterable[str]) -> None: - self.buffer.writelines(x.encode(self.encoding, "replace") for x in lines) +class EncodedFile(io.TextIOWrapper): + __slots__ = () @property def name(self) -> str: - """Ensure that file.name is a string.""" + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 return repr(self.buffer) @property def mode(self) -> str: + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. return self.buffer.mode.replace("b", "") - def __getattr__(self, name): - return getattr(object.__getattribute__(self, "buffer"), name) - CaptureResult = collections.namedtuple("CaptureResult", ["out", "err"]) @@ -552,9 +519,12 @@ class FDCaptureBinary: self.syscapture = SysCapture(targetfd) else: if tmpfile is None: - f = TemporaryFile() - with f: - tmpfile = safe_text_dupfile(f, mode="wb+") + tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + write_through=True, + ) if targetfd in patchsysdict: self.syscapture = SysCapture(targetfd, tmpfile) else: @@ -583,7 +553,7 @@ class FDCaptureBinary: def snap(self): self.tmpfile.seek(0) - res = self.tmpfile.read() + res = self.tmpfile.buffer.read() self.tmpfile.seek(0) self.tmpfile.truncate() return res @@ -623,10 +593,10 @@ class FDCapture(FDCaptureBinary): EMPTY_BUFFER = str() # type: ignore def snap(self): - res = super().snap() - enc = getattr(self.tmpfile, "encoding", None) - if enc and isinstance(res, bytes): - res = str(res, enc, "replace") + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() return res def writeorg(self, data): Index: pytest-5.4.3/testing/test_capture.py =================================================================== --- pytest-5.4.3.orig/testing/test_capture.py +++ pytest-5.4.3/testing/test_capture.py @@ -887,49 +887,6 @@ def tmpfile(testdir) -> Generator[Binary f.close() -@needsosdup -def test_dupfile(tmpfile) -> None: - flist = [] # type: List[TextIO] - for i in range(5): - nf = capture.safe_text_dupfile(tmpfile, "wb") - assert nf != tmpfile - assert nf.fileno() != tmpfile.fileno() - assert nf not in flist - print(i, end="", file=nf) - flist.append(nf) - - fname_open = flist[0].name - assert fname_open == repr(flist[0].buffer) - - for i in range(5): - f = flist[i] - f.close() - fname_closed = flist[0].name - assert fname_closed == repr(flist[0].buffer) - assert fname_closed != fname_open - tmpfile.seek(0) - s = tmpfile.read() - assert "01234" in repr(s) - tmpfile.close() - assert fname_closed == repr(flist[0].buffer) - - -def test_dupfile_on_bytesio(): - bio = io.BytesIO() - f = capture.safe_text_dupfile(bio, "wb") - f.write("hello") - assert bio.getvalue() == b"hello" - assert "BytesIO object" in f.name - - -def test_dupfile_on_textio(): - sio = StringIO() - f = capture.safe_text_dupfile(sio, "wb") - f.write("hello") - assert sio.getvalue() == "hello" - assert not hasattr(f, "name") - - @contextlib.contextmanager def lsof_check(): pid = os.getpid() @@ -1377,8 +1334,8 @@ def test_error_attribute_issue555(testdi """ import sys def test_capattr(): - assert sys.stdout.errors == "strict" - assert sys.stderr.errors == "strict" + assert sys.stdout.errors == "replace" + assert sys.stderr.errors == "replace" """ ) reprec = testdir.inline_run() @@ -1453,15 +1410,6 @@ def test_crash_on_closing_tmpfile_py27(t result.stdout.no_fnmatch_line("*IOError*") -def test_pickling_and_unpickling_encoded_file(): - # See https://bitbucket.org/pytest-dev/pytest/pull-request/194 - # pickle.loads() raises infinite recursion if - # EncodedFile.__getattr__ is not implemented properly - ef = capture.EncodedFile(None, None) - ef_as_str = pickle.dumps(ef) - pickle.loads(ef_as_str) - - def test_global_capture_with_live_logging(testdir): # Issue 3819 # capture should work with live cli logging @@ -1578,12 +1526,13 @@ def test_stderr_write_returns_len(capsys def test_encodedfile_writelines(tmpfile: BinaryIO) -> None: - ef = capture.EncodedFile(tmpfile, "utf-8") - with pytest.raises(AttributeError): - ef.writelines([b"line1", b"line2"]) # type: ignore[list-item] # noqa: F821 - assert ef.writelines(["line1", "line2"]) is None # type: ignore[func-returns-value] # noqa: F821 + ef = capture.EncodedFile(tmpfile, encoding="utf-8") + with pytest.raises(TypeError): + ef.writelines([b"line1", b"line2"]) + assert ef.writelines(["line3", "line4"]) is None # type: ignore[func-returns-value] # noqa: F821 + ef.flush() tmpfile.seek(0) - assert tmpfile.read() == b"line1line2" + assert tmpfile.read() == b"line3line4" tmpfile.close() with pytest.raises(ValueError): ef.read()