Skip to content

Commit 9bb1215

Browse files
authored
Merge pull request #174 from ngoldbaum/avoid-crash
Avoid crash for sybil doctests
2 parents 4169fca + 4b604b2 commit 9bb1215

2 files changed

Lines changed: 54 additions & 8 deletions

File tree

src/pytest_run_parallel/plugin.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,13 @@ def pytest_collection_finish(self, session):
254254
self._handle_collected_item(item)
255255

256256
def _handle_collected_item(self, item):
257-
if not hasattr(item, "obj"):
257+
if isinstance(item, _pytest.doctest.DoctestItem):
258+
self._mark_test_thread_unsafe(
259+
item, "is a doctest (pytest-run-parallel does not support doctests)"
260+
)
261+
return
262+
263+
if getattr(item, "obj", None) is None:
258264
if not hasattr(item, "_parallel_custom_item"):
259265
warnings.warn(
260266
f"Encountered pytest item with type {type(item)} with no 'obj' "
@@ -273,12 +279,6 @@ def _handle_collected_item(self, item):
273279
)
274280
return
275281

276-
if isinstance(item, _pytest.doctest.DoctestItem):
277-
self._mark_test_thread_unsafe(
278-
item, "is a doctest (pytest-run-parallel does not support doctests)"
279-
)
280-
return
281-
282282
n_workers, parallel_threads_marker_used = get_num_workers(item)
283283
if n_workers < 0:
284284
raise ValueError("parallel-threads cannot be negative")

tests/test_run_parallel.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,10 @@ def test_should_skip():
579579
)
580580

581581

582-
def test_incompatible_test_item(pytester):
582+
def test_incompatible_test_item_no_obj_item(pytester):
583+
"""Doctests and other doctest-like custom pytest.Item subclasses are incompatible
584+
without an obj attribute on the Item. This shouldn't crash pytest-run-parallel
585+
and the test should only run on one thread"""
583586
pytester.makeconftest("""
584587
import inspect
585588
import pytest
@@ -615,7 +618,50 @@ def test_incompatible_item():
615618
assert result.parseoutcomes()["warnings"] == 1
616619

617620

621+
def test_incompatible_test_item_obj_is_none(pytester):
622+
"""Doctests and other doctest-like custom pytest.Item subclasses are incompatible
623+
if the obj attribute is None. This shouldn't crash pytest-run-parallel
624+
and the test should only run on one thread"""
625+
pytester.makeconftest("""
626+
import inspect
627+
import pytest
628+
629+
class CustomItem(pytest.Item):
630+
def __init__(self, name, parent=None, config=None, session=None, nodeid=None, function=None, **kwargs):
631+
super().__init__(name, parent, config, session, nodeid, **kwargs)
632+
self.function = function
633+
self.obj = None
634+
635+
def runtest(self):
636+
self.function()
637+
638+
@pytest.hookimpl(wrapper=True, trylast=True)
639+
def pytest_pycollect_makeitem(collector, name: str, obj: object):
640+
result = yield
641+
if not inspect.isfunction(obj):
642+
return result
643+
return CustomItem.from_parent(name=name, parent=collector, function=obj)
644+
""")
645+
646+
pytester.makepyfile("""
647+
import pytest
648+
649+
def test_incompatible_item():
650+
assert True
651+
""")
652+
result = pytester.runpytest("--parallel-threads=10", "-v", "-W", "default")
653+
result.stdout.fnmatch_lines(
654+
[
655+
"*::test_incompatible_item PASSED*",
656+
]
657+
)
658+
assert result.parseoutcomes()["warnings"] == 1
659+
result.stdout.fnmatch_lines(["*Encountered pytest item * with no 'obj' attribute*"])
660+
661+
618662
def test_known_incompatible_test_item_doesnt_warn(pytester):
663+
"""If the Item has a _parallel_custom_item attribute, pytest-run-parallel
664+
shouldn't warn about the incompatibility"""
619665
pytester.makeconftest("""
620666
import inspect
621667
import pytest

0 commit comments

Comments
 (0)