-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
I ran into this while tracking down another issue.
In TerminalReporter.pytest_runtest_logstart, we have:
pytest/src/_pytest/terminal.py
Lines 507 to 513 in adc1974
| 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:
pytest/src/_pytest/terminal.py
Lines 867 to 884 in adc1974
| 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) |
Lines 680 to 698 in adc1974
| 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.