Skip to content

str/Path mixup in TerminalReporter #8990

@chebee7i

Description

@chebee7i

I ran into this while tracking down another issue.

In TerminalReporter.pytest_runtest_logstart, we have:

def pytest_runtest_logstart(
self, nodeid: str, location: Tuple[str, Optional[int], str]
) -> None:
# Ensure that the path is printed before the
# 1st test of a module starts running.
if self.showlongtestinfo:
line = self._locationline(nodeid, *location)

where the first argument of location is expected to be a string. But the func signature and impl of _locationline and pathlib.bestrelpath are actually expecting a Path object:

def _locationline(self, nodeid, fspath, lineno, domain):
def mkrel(nodeid):
line = self.config.cwd_relative_nodeid(nodeid)
if domain and line.endswith(domain):
line = line[: -len(domain)]
values = domain.split("[")
values[0] = values[0].replace(".", "::") # don't replace '.' in params
line += "[".join(values)
return line
# collect_fspath comes from testid which has a "/"-normalized path.
if fspath:
res = mkrel(nodeid)
if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace(
"\\", nodes.SEP
):
res += " <- " + bestrelpath(self.startpath, fspath)

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 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)
reldest = dest.relative_to(base)

Due to the __tracebackhide__ = True line, this caused a harder-to-trackdown exception:

AttributeError: 'str' object has no attribute 'relative_to'

Apologies that I don't have a simple reproducible example (the code that generates this is a bit involved, bringing in twisted etc).
I experienced this when my report had location attribute that was of the following form: (str, int, str) right before calling: session.ihook.pytest_runtest_logreport(report=report) in Python 3.8.6 with pytest 3.6.4 (and the code snippets above are from master, so I know the issue still exists). The situation was that nodeid.split("::")[0] != fspath.replace("\\", nodes.SEP), so we needed to call bestrelpath which requires a Path object.

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: reportingrelated to terminal output and user-facing messages and errorstype: bugproblem that needs to be addressed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions