From db08c7fbb049592ef81485ceb40d41e5a3265840 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 29 Sep 2020 13:11:47 +0300 Subject: [PATCH 1/3] pathlib: improve comments on commonpath and bestrelpath --- src/_pytest/pathlib.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 355281039..20516745d 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -581,7 +581,10 @@ def absolutepath(path: Union[Path, str]) -> Path: def commonpath(path1: Path, path2: Path) -> Optional[Path]: """Return the common part shared with the other path, or None if there is - no common part.""" + no common part. + + If one path is relative and one is absolute, returns None. + """ try: return Path(os.path.commonpath((str(path1), str(path2)))) except ValueError: @@ -592,13 +595,17 @@ def bestrelpath(directory: Path, dest: Path) -> str: """Return a string which is a relative path from directory to dest such that directory/bestrelpath == dest. + The paths must be either both absolute or both relative. + If no such path can be determined, returns dest. """ if dest == directory: return os.curdir # Find the longest common directory. base = commonpath(directory, dest) - # Can be the case on Windows. + # Can be the case on Windows for two absolute paths on different drives. + # Can be the case for two relative paths without common prefix. + # Can be the case for a relative path and an absolute path. if not base: return str(dest) reldirectory = directory.relative_to(base) From 61f80a783a38441040c5999a4033af8004ee4c6b Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 29 Sep 2020 09:29:27 +0300 Subject: [PATCH 2/3] terminal: fix crash in header reporting when absolute testpaths is used Regressed in 6.1.0 in 62e249a1f934d1073c9a0167077e133c5e0f6270. The `x` is an `str` but is expected to be a `pathlib.Path`. Not caught by mypy because `config.getini()` returns `Any`. Fix by just removing the `bestrelpath` call: - testpaths are always relative to the rootdir, it thus would be very unusual to specify an absolute path there. - The code was wrong even before the regression: `py.path.local`'s `bestrelpath` function expects a `py.path.local`, not an `str`. But it had some weird `try ... except AttributeError` fallback which just returns the argument, i.e. it was a no-op. So there is no behavior change. - It seems reasonable to me to just print the full path if that's what the ini specifies. --- changelog/7814.bugfix.rst | 1 + src/_pytest/terminal.py | 6 +++--- testing/test_terminal.py | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 changelog/7814.bugfix.rst diff --git a/changelog/7814.bugfix.rst b/changelog/7814.bugfix.rst new file mode 100644 index 000000000..a5f2a9a95 --- /dev/null +++ b/changelog/7814.bugfix.rst @@ -0,0 +1 @@ +Fixed crash in header reporting when :confval:`testpaths` is used and contains absolute paths (regression in 6.1.0). diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index e059612c2..34933ad21 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -718,10 +718,10 @@ class TerminalReporter: if config.inipath: line += ", configfile: " + bestrelpath(config.rootpath, config.inipath) - testpaths = config.getini("testpaths") + testpaths = config.getini("testpaths") # type: List[str] if testpaths and config.args == testpaths: - rel_paths = [bestrelpath(config.rootpath, x) for x in testpaths] - line += ", testpaths: {}".format(", ".join(rel_paths)) + line += ", testpaths: {}".format(", ".join(testpaths)) + result = [line] plugininfo = config.pluginmanager.list_plugin_distinfo() diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 57db1b9a5..51fdf728e 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -18,6 +18,7 @@ import pytest from _pytest._io.wcwidth import wcswidth from _pytest.config import Config from _pytest.config import ExitCode +from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import Path from _pytest.pytester import Testdir from _pytest.reports import BaseReport @@ -749,6 +750,29 @@ class TestTerminalFunctional: result = testdir.runpytest("tests") result.stdout.fnmatch_lines(["rootdir: *test_header0, configfile: tox.ini"]) + def test_header_absolute_testpath( + self, testdir: Testdir, monkeypatch: MonkeyPatch + ) -> None: + """Regresstion test for #7814.""" + tests = testdir.tmpdir.join("tests") + tests.ensure_dir() + testdir.makepyprojecttoml( + """ + [tool.pytest.ini_options] + testpaths = ['{}'] + """.format( + tests + ) + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines( + [ + "rootdir: *absolute_testpath0, configfile: pyproject.toml, testpaths: {}".format( + tests + ) + ] + ) + def test_no_header(self, testdir): testdir.tmpdir.join("tests").ensure_dir() testdir.tmpdir.join("gui").ensure_dir() From 3ecdad67b769c963fe08a7da41650d4a6262935c Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 29 Sep 2020 13:25:34 +0300 Subject: [PATCH 3/3] terminal: improve condition on whether to display testpaths in header Make it match better the condition on whether testpaths is used (found in config/__init__.py). --- src/_pytest/terminal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 34933ad21..2e7ebded6 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -719,7 +719,7 @@ class TerminalReporter: line += ", configfile: " + bestrelpath(config.rootpath, config.inipath) testpaths = config.getini("testpaths") # type: List[str] - if testpaths and config.args == testpaths: + if config.invocation_params.dir == config.rootpath and config.args == testpaths: line += ", testpaths: {}".format(", ".join(testpaths)) result = [line]