Improve way in which skip location is fixed up when skipped by mark
When `pytest.skip()` is called inside a test function, the skip location should be reported as the line that made the call, however when `pytest.skip()` is called by the `pytest.mark.skip` and similar mechanisms, the location should be reported at the item's location, because the exact location is some irrelevant internal code. Currently the item-location case is implemented by the caller setting a boolean key on the item's store and the `skipping` plugin checking it and fixing up the location if needed. This is really roundabout IMO and breaks encapsulation. Instead, allow the caller to specify directly on the skip exception whether to use the item's location or not. For now, this is entirely private.
This commit is contained in:
parent
3dde519f53
commit
7f989203ed
|
@ -58,9 +58,14 @@ class Skipped(OutcomeException):
|
|||
msg: Optional[str] = None,
|
||||
pytrace: bool = True,
|
||||
allow_module_level: bool = False,
|
||||
*,
|
||||
_use_item_location: bool = False,
|
||||
) -> None:
|
||||
OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
|
||||
self.allow_module_level = allow_module_level
|
||||
# If true, the skip location is reported as the item's location,
|
||||
# instead of the place that raises the exception/calls skip().
|
||||
self._use_item_location = _use_item_location
|
||||
|
||||
|
||||
class Failed(OutcomeException):
|
||||
|
|
|
@ -324,7 +324,12 @@ class TestReport(BaseReport):
|
|||
elif isinstance(excinfo.value, skip.Exception):
|
||||
outcome = "skipped"
|
||||
r = excinfo._getreprcrash()
|
||||
longrepr = (str(r.path), r.lineno, r.message)
|
||||
if excinfo.value._use_item_location:
|
||||
filename, line = item.reportinfo()[:2]
|
||||
assert line is not None
|
||||
longrepr = str(filename), line + 1, r.message
|
||||
else:
|
||||
longrepr = (str(r.path), r.lineno, r.message)
|
||||
else:
|
||||
outcome = "failed"
|
||||
if call.when == "call":
|
||||
|
|
|
@ -230,8 +230,6 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]:
|
|||
return None
|
||||
|
||||
|
||||
# Whether skipped due to skip or skipif marks.
|
||||
skipped_by_mark_key = StoreKey[bool]()
|
||||
# Saves the xfail mark evaluation. Can be refreshed during call if None.
|
||||
xfailed_key = StoreKey[Optional[Xfail]]()
|
||||
|
||||
|
@ -239,9 +237,8 @@ xfailed_key = StoreKey[Optional[Xfail]]()
|
|||
@hookimpl(tryfirst=True)
|
||||
def pytest_runtest_setup(item: Item) -> None:
|
||||
skipped = evaluate_skip_marks(item)
|
||||
item._store[skipped_by_mark_key] = skipped is not None
|
||||
if skipped:
|
||||
skip(skipped.reason)
|
||||
raise skip.Exception(skipped.reason, _use_item_location=True)
|
||||
|
||||
item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item)
|
||||
if xfailed and not item.config.option.runxfail and not xfailed.run:
|
||||
|
@ -292,19 +289,6 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
|
|||
rep.outcome = "passed"
|
||||
rep.wasxfail = xfailed.reason
|
||||
|
||||
if (
|
||||
item._store.get(skipped_by_mark_key, True)
|
||||
and rep.skipped
|
||||
and type(rep.longrepr) is tuple
|
||||
):
|
||||
# Skipped by mark.skipif; change the location of the failure
|
||||
# to point to the item definition, otherwise it will display
|
||||
# the location of where the skip exception was raised within pytest.
|
||||
_, _, reason = rep.longrepr
|
||||
filename, line = item.reportinfo()[:2]
|
||||
assert line is not None
|
||||
rep.longrepr = str(filename), line + 1, reason
|
||||
|
||||
|
||||
def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]:
|
||||
if hasattr(report, "wasxfail"):
|
||||
|
|
|
@ -29,7 +29,6 @@ from _pytest.python import Class
|
|||
from _pytest.python import Function
|
||||
from _pytest.python import PyCollector
|
||||
from _pytest.runner import CallInfo
|
||||
from _pytest.skipping import skipped_by_mark_key
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import unittest
|
||||
|
@ -150,7 +149,7 @@ def _make_xunit_fixture(
|
|||
def fixture(self, request: FixtureRequest) -> Generator[None, None, None]:
|
||||
if _is_skipped(self):
|
||||
reason = self.__unittest_skip_why__
|
||||
pytest.skip(reason)
|
||||
raise pytest.skip.Exception(reason, _use_item_location=True)
|
||||
if setup is not None:
|
||||
try:
|
||||
if pass_self:
|
||||
|
@ -256,9 +255,8 @@ class TestCaseFunction(Function):
|
|||
|
||||
def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None:
|
||||
try:
|
||||
skip(reason)
|
||||
raise pytest.skip.Exception(reason, _use_item_location=True)
|
||||
except skip.Exception:
|
||||
self._store[skipped_by_mark_key] = True
|
||||
self._addexcinfo(sys.exc_info())
|
||||
|
||||
def addExpectedFailure(
|
||||
|
|
Loading…
Reference in New Issue