Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/sybil/integration/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ def runtest(self) -> None:
def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback:
traceback = excinfo.traceback
tb = traceback.cut(path=example_module_path)
tb_entry = tb[1]
if getattr(tb_entry, '_rawentry', None) is not None:
traceback = Traceback(tb_entry._rawentry)
# ``tb`` has a single entry when the example frame is the deepest in
# the traceback, leaving no frame below it to re-root at.
if len(tb) > 1:
tb_entry = tb[1]
if getattr(tb_entry, '_rawentry', None) is not None:
traceback = Traceback(tb_entry._rawentry)
return traceback

def repr_failure(
Expand Down
34 changes: 32 additions & 2 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import sys
from inspect import getsourcefile
from os.path import abspath
from pathlib import Path

import pytest
from pytest import CaptureFixture
from pytest import CaptureFixture, ExceptionInfo
from testfixtures import Replacer, compare, not_there

from sybil import Sybil
from sybil import Sybil, example as example_module
from sybil.document import Document
from sybil.integration.pytest import SybilItem
from sybil.parsers.rest import PythonCodeBlockParser, DocTestParser
from sybil.python import import_cleanup
from .helpers import (
Expand Down Expand Up @@ -131,6 +135,32 @@ def test_pytest(capsys: CaptureFixture[str]):
assert results.total == 10


def test_traceback_filter_single_entry_cut() -> None:
# A failure whose deepest frame is the example frame cuts to a single
# traceback entry; filtering it must not raise IndexError.
source = getsourcefile(example_module)
assert source is not None
example_path = abspath(source)

document = Document(".. code-block:: python\n\n raise ValueError('boom')\n", "x.rst")
for region in PythonCodeBlockParser()(document):
document.add(region)

with pytest.raises(ValueError) as excinfo:
next(iter(document.examples())).evaluate()

# Truncate so the example frame is the deepest, collapsing the cut to one.
raw = excinfo.tb
while raw is not None and raw.tb_frame.f_code.co_filename != example_path:
raw = raw.tb_next
assert raw is not None
raw.tb_next = None

truncated = ExceptionInfo.from_exc_info((excinfo.type, excinfo.value, raw))
result = SybilItem.__new__(SybilItem)._traceback_filter(truncated)
assert len(result) == 1


def test_pytest_output_immune_to_color_env(capsys: CaptureFixture[str]):
with Replacer() as replace:
replace.in_environ('PY_COLORS', not_there)
Expand Down
Loading