Skip to content

Commit 7f98920

Browse files
committed
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.
1 parent 3dde519 commit 7f98920

File tree

4 files changed

+14
-22
lines changed

4 files changed

+14
-22
lines changed

src/_pytest/outcomes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ def __init__(
5858
msg: Optional[str] = None,
5959
pytrace: bool = True,
6060
allow_module_level: bool = False,
61+
*,
62+
_use_item_location: bool = False,
6163
) -> None:
6264
OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
6365
self.allow_module_level = allow_module_level
66+
# If true, the skip location is reported as the item's location,
67+
# instead of the place that raises the exception/calls skip().
68+
self._use_item_location = _use_item_location
6469

6570

6671
class Failed(OutcomeException):

src/_pytest/reports.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,12 @@ def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport":
324324
elif isinstance(excinfo.value, skip.Exception):
325325
outcome = "skipped"
326326
r = excinfo._getreprcrash()
327-
longrepr = (str(r.path), r.lineno, r.message)
327+
if excinfo.value._use_item_location:
328+
filename, line = item.reportinfo()[:2]
329+
assert line is not None
330+
longrepr = str(filename), line + 1, r.message
331+
else:
332+
longrepr = (str(r.path), r.lineno, r.message)
328333
else:
329334
outcome = "failed"
330335
if call.when == "call":

src/_pytest/skipping.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,15 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]:
230230
return None
231231

232232

233-
# Whether skipped due to skip or skipif marks.
234-
skipped_by_mark_key = StoreKey[bool]()
235233
# Saves the xfail mark evaluation. Can be refreshed during call if None.
236234
xfailed_key = StoreKey[Optional[Xfail]]()
237235

238236

239237
@hookimpl(tryfirst=True)
240238
def pytest_runtest_setup(item: Item) -> None:
241239
skipped = evaluate_skip_marks(item)
242-
item._store[skipped_by_mark_key] = skipped is not None
243240
if skipped:
244-
skip(skipped.reason)
241+
raise skip.Exception(skipped.reason, _use_item_location=True)
245242

246243
item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item)
247244
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]):
292289
rep.outcome = "passed"
293290
rep.wasxfail = xfailed.reason
294291

295-
if (
296-
item._store.get(skipped_by_mark_key, True)
297-
and rep.skipped
298-
and type(rep.longrepr) is tuple
299-
):
300-
# Skipped by mark.skipif; change the location of the failure
301-
# to point to the item definition, otherwise it will display
302-
# the location of where the skip exception was raised within pytest.
303-
_, _, reason = rep.longrepr
304-
filename, line = item.reportinfo()[:2]
305-
assert line is not None
306-
rep.longrepr = str(filename), line + 1, reason
307-
308292

309293
def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]:
310294
if hasattr(report, "wasxfail"):

src/_pytest/unittest.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
from _pytest.python import Function
3030
from _pytest.python import PyCollector
3131
from _pytest.runner import CallInfo
32-
from _pytest.skipping import skipped_by_mark_key
3332

3433
if TYPE_CHECKING:
3534
import unittest
@@ -150,7 +149,7 @@ def cleanup(*args):
150149
def fixture(self, request: FixtureRequest) -> Generator[None, None, None]:
151150
if _is_skipped(self):
152151
reason = self.__unittest_skip_why__
153-
pytest.skip(reason)
152+
raise pytest.skip.Exception(reason, _use_item_location=True)
154153
if setup is not None:
155154
try:
156155
if pass_self:
@@ -256,9 +255,8 @@ def addFailure(
256255

257256
def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None:
258257
try:
259-
skip(reason)
258+
raise pytest.skip.Exception(reason, _use_item_location=True)
260259
except skip.Exception:
261-
self._store[skipped_by_mark_key] = True
262260
self._addexcinfo(sys.exc_info())
263261

264262
def addExpectedFailure(

0 commit comments

Comments
 (0)