Files
app-store-optimization/skills/loki-mode/benchmarks/results/2026-01-05-10-37-54/swebench-loki-patches/pytest-dev__pytest-7220.patch

391 lines
15 KiB
Diff

# pytest-dev__pytest-7220
# Loki Mode Multi-Agent Patch
# Attempts: 1
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -1,4 +1,5 @@
import inspect
+import os
import re
import sys
import traceback
@@ -551,13 +552,27 @@ class ReprFileLocation(TerminalRepr):
self.lineno = lineno
self.message = message
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter, invocation_dir=None) -> None:
# filename and samples of underline
msg = self.message
- i = msg.find("\n")
- if i != -1:
- msg = msg[:i]
- tw.write(self.path, bold=True, red=True)
+ lineno = self.lineno
+ path = self.path
+
+ # Compute path relative to invocation directory if available
+ if invocation_dir is not None:
+ try:
+ abspath = os.path.abspath(path)
+ relpath = os.path.relpath(abspath, str(invocation_dir))
+ # Use relative path unless it goes too far up
+ if not relpath.startswith(".."):
+ path = relpath
+ else:
+ path = abspath
+ except (ValueError, OSError):
+ pass
+
+ tw.write(path, bold=True, red=True)
tw.line(":{}: {}".format(self.lineno, msg))
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -558,6 +558,10 @@ class ReprFileLocation(TerminalRepr):
lineno = self.lineno
path = self.path
+ i = msg.find("\n")
+ if i != -1:
+ msg = msg[:i]
+
# Compute path relative to invocation directory if available
if invocation_dir is not None:
try:
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -992,7 +992,7 @@ class TerminalReporter:
for reprlocation_lines in rep.longrepr.chain:
for reprlocation, lines, style in reprlocation_lines:
if reprlocation:
- reprlocation.toterminal(self._tw)
+ reprlocation.toterminal(self._tw, self.config.invocation_dir)
for line in lines:
self._tw.line(line)
@@ -1003,6 +1003,9 @@ class TerminalReporter:
for line in rep.longrepr.splitlines():
self._tw.line(line)
else:
- rep.longrepr.toterminal(self._tw)
+ if hasattr(rep.longrepr, 'toterminal'):
+ rep.longrepr.toterminal(self._tw)
+ else:
+ self._tw.line(str(rep.longrepr))
def _folded_skips(self) -> List[Tuple[int, str, Optional[int], str]]:
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -991,16 +991,7 @@ class TerminalReporter:
if hasattr(rep.longrepr, "chain"):
for reprlocation_lines in rep.longrepr.chain:
- for reprlocation, lines, style in reprlocation_lines:
- if reprlocation:
- reprlocation.toterminal(self._tw, self.config.invocation_dir)
- for line in lines:
- self._tw.line(line)
-
- for line in rep.longrepr.chain[-1][1]:
- self._tw.line(line)
- elif isinstance(rep.longrepr, str):
- for line in rep.longrepr.splitlines():
- self._tw.line(line)
- else:
- if hasattr(rep.longrepr, 'toterminal'):
- rep.longrepr.toterminal(self._tw)
- else:
- self._tw.line(str(rep.longrepr))
+ rep.longrepr.toterminal(self._tw)
+ elif isinstance(rep.longrepr, str):
+ for line in rep.longrepr.splitlines():
+ self._tw.line(line)
+ else:
+ rep.longrepr.toterminal(self._tw)
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -476,6 +476,7 @@ class ExceptionChainRepr(ExceptionRepr):
super().__init__()
self.chain = chain
# reprcrash and reprtraceback of the outermost (the newest) parsing exception
+ self.invocation_dir = None
self.reprtraceback = chain[-1][0]
self.reprcrash = chain[-1][1]
@@ -485,6 +486,8 @@ class ExceptionChainRepr(ExceptionRepr):
return "<{} tbstyle={} {!r}>".format(cls, style, self.chain)
def toterminal(self, tw: TerminalWriter) -> None:
+ invocation_dir = getattr(self, 'invocation_dir', None)
+
for element in self.chain:
reprtraceback, reprcrash, descr = element
if descr:
@@ -492,7 +495,7 @@ class ExceptionChainRepr(ExceptionRepr):
reprtraceback.toterminal(tw)
if reprcrash:
# Add empty line between traceback and file location
- reprcrash.toterminal(tw)
+ reprcrash.toterminal(tw, invocation_dir)
class ReprTraceback(TerminalRepr):
--- a/src/_pytest/reports.py
+++ b/src/_pytest/reports.py
@@ -5,6 +5,7 @@ from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
+from pathlib import Path
import py
@@ -131,6 +132,17 @@ class BaseReport:
writer.line("")
self.longrepr.toterminal(writer)
+ def set_invocation_dir(self, invocation_dir: Path) -> None:
+ """Set the invocation directory for path computation in error messages.
+
+ This is used to ensure file paths in error messages are always relative
+ to the original invocation directory, not the current working directory.
+ """
+ if hasattr(self.longrepr, 'chain'):
+ self.longrepr.invocation_dir = invocation_dir
+ if hasattr(self.longrepr, 'reprcrash'):
+ self.longrepr.invocation_dir = invocation_dir
+
def _get_verbose_word(self, config):
_category, _short, verbose = config.hook.pytest_report_teststatus(
report=self, config=config
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -994,6 +994,9 @@ class TerminalReporter:
for rep in reports:
if rep.outcome == "failed":
self._outrep_summary(rep)
+ if hasattr(rep.longrepr, 'invocation_dir'):
+ rep.longrepr.invocation_dir = self.config.invocation_dir
+ self._outrep_summary(rep)
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -991,9 +991,11 @@ class TerminalReporter:
def short_test_summary(self) -> None:
if not self.reportchars:
return
-
+
for rep in self._getfailures():
msg = self._getfailrepr(rep)
+ if hasattr(rep.longrepr, 'invocation_dir'):
+ rep.longrepr.invocation_dir = self.config.invocation_dir
self.write_sep("_", "FAILURES")
for rep in self.stats.get("failed", []):
if self.verbosity <= 0:
@@ -1001,6 +1003,8 @@ class TerminalReporter:
else:
self._outrep_summary(rep)
+ def _outrep_summary(self, rep: "BaseReport") -> None:
+ """Print the failure / error summary for a report."""
if hasattr(rep.longrepr, "chain"):
rep.longrepr.toterminal(self._tw)
elif isinstance(rep.longrepr, str):
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -982,6 +982,8 @@ class TerminalReporter:
self._tw.line(line)
def _outrep_summary(self, rep: "BaseReport") -> None:
+ if hasattr(rep.longrepr, 'invocation_dir') and rep.longrepr.invocation_dir is None:
+ rep.longrepr.invocation_dir = self.config.invocation_dir
rep.toterminal(self._tw)
def summary_failures(self) -> None:
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -551,7 +551,7 @@ class ReprFileLocation(TerminalRepr):
self.lineno = lineno
self.message = message
- def toterminal(self, tw: TerminalWriter, invocation_dir=None) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
# filename and samples of underline
msg = self.message
lineno = self.lineno
@@ -561,17 +561,7 @@ class ReprFileLocation(TerminalRepr):
if i != -1:
msg = msg[:i]
- # Compute path relative to invocation directory if available
- if invocation_dir is not None:
- try:
- abspath = os.path.abspath(path)
- relpath = os.path.relpath(abspath, str(invocation_dir))
- # Use relative path unless it goes too far up
- if not relpath.startswith(".."):
- path = relpath
- else:
- path = abspath
- except (ValueError, OSError):
- pass
-
tw.write(path, bold=True, red=True)
tw.line(":{}: {}".format(self.lineno, msg))
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -485,7 +485,6 @@ class ExceptionChainRepr(ExceptionRepr):
return "<{} tbstyle={} {!r}>".format(cls, style, self.chain)
def toterminal(self, tw: TerminalWriter) -> None:
- invocation_dir = getattr(self, 'invocation_dir', None)
-
for element in self.chain:
reprtraceback, reprcrash, descr = element
if descr:
@@ -493,7 +492,7 @@ class ExceptionChainRepr(ExceptionRepr):
reprtraceback.toterminal(tw)
if reprcrash:
# Add empty line between traceback and file location
- reprcrash.toterminal(tw, invocation_dir)
+ reprcrash.toterminal(tw)
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -475,7 +475,6 @@ class ExceptionChainRepr(ExceptionRepr):
) -> None:
super().__init__()
self.chain = chain
- self.invocation_dir = None
# reprcrash and reprtraceback of the outermost (the newest) parsing exception
self.reprtraceback = chain[-1][0]
self.reprcrash = chain[-1][1]
--- a/src/_pytest/reports.py
+++ b/src/_pytest/reports.py
@@ -5,7 +5,6 @@ from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
-from pathlib import Path
import py
@@ -132,17 +131,6 @@ class BaseReport:
writer.line("")
self.longrepr.toterminal(writer)
- def set_invocation_dir(self, invocation_dir: Path) -> None:
- """Set the invocation directory for path computation in error messages.
-
- This is used to ensure file paths in error messages are always relative
- to the original invocation directory, not the current working directory.
- """
- if hasattr(self.longrepr, 'chain'):
- self.longrepr.invocation_dir = invocation_dir
- if hasattr(self.longrepr, 'reprcrash'):
- self.longrepr.invocation_dir = invocation_dir
-
def _get_verbose_word(self, config):
_category, _short, verbose = config.hook.pytest_report_teststatus(
report=self, config=config
--- a/src/_pytest/terminal.py
+++ b/src/_pytest/terminal.py
@@ -982,8 +982,6 @@ class TerminalReporter:
self._tw.line(line)
def _outrep_summary(self, rep: "BaseReport") -> None:
- if hasattr(rep.longrepr, 'invocation_dir') and rep.longrepr.invocation_dir is None:
- rep.longrepr.invocation_dir = self.config.invocation_dir
rep.toterminal(self._tw)
def summary_failures(self) -> None:
--- a/src/_pytest/nodes.py
+++ b/src/_pytest/nodes.py
@@ -323,7 +323,15 @@ class Node:
excinfo: ExceptionInfo[Union[BaseException, Entry]],
style: "Optional[str]" = None,
) -> "Union[str, TerminalRepr]":
- return excinfo.getrepr(
+ """Return a representation of the exception info for terminal output.
+
+ This stores the invocation directory in the representation to ensure
+ file paths are always relative to the original directory, even if a
+ fixture changes the working directory.
+ """
+ invocation_dir = self.config.invocation_dir
+
+ repr_result = excinfo.getrepr(
funcargs=True,
abspath=False,
showlocals=self.config.option.showlocals,
@@ -333,6 +341,11 @@ class Node:
truncate_locals=True,
)
+ # Store invocation_dir on the representation for later use
+ if hasattr(repr_result, 'reprtraceback'):
+ repr_result.invocation_dir = invocation_dir
+ return repr_result
+
# Methods for ordering nodes
@property
def nodeid(self) -> str:
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -475,6 +475,7 @@ class ExceptionChainRepr(ExceptionRepr):
) -> None:
super().__init__()
self.chain = chain
+ self.invocation_dir = None
# reprcrash and reprtraceback of the outermost (the newest) parsing exception
self.reprtraceback = chain[-1][0]
self.reprcrash = chain[-1][1]
@@ -485,11 +486,13 @@ class ExceptionChainRepr(ExceptionRepr):
return "<{} tbstyle={} {!r}>".format(cls, style, self.chain)
def toterminal(self, tw: TerminalWriter) -> None:
+ invocation_dir = getattr(self, 'invocation_dir', None)
+
for element in self.chain:
reprtraceback, reprcrash, descr = element
if descr:
tw.line(descr, yellow=True)
reprtraceback.toterminal(tw)
if reprcrash:
- # Add empty line between traceback and file location
- reprcrash.toterminal(tw)
+ reprcrash.toterminal(tw, invocation_dir)
class ReprTraceback(TerminalRepr):
--- a/src/_pytest/_code/code.py
+++ b/src/_pytest/_code/code.py
@@ -551,7 +551,7 @@ class ReprFileLocation(TerminalRepr):
self.lineno = lineno
self.message = message
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter, invocation_dir=None) -> None:
# filename and samples of underline
msg = self.message
lineno = self.lineno
@@ -561,6 +561,18 @@ class ReprFileLocation(TerminalRepr):
if i != -1:
msg = msg[:i]
+ # Compute path relative to invocation directory if available
+ if invocation_dir is not None:
+ try:
+ abspath = os.path.abspath(path)
+ relpath = os.path.relpath(abspath, str(invocation_dir))
+ # Use relative path if it doesn't go up too many directories
+ if not relpath.startswith(".."):
+ path = relpath
+ else:
+ path = abspath
+ except (ValueError, OSError):
+ pass
+
tw.write(path, bold=True, red=True)
tw.line(":{}: {}".format(self.lineno, msg))