So my last iteration is here. It felt unintuitive to use pycollect_makeitem, but seems to be the solution, after all. If you see any flaws I would be grateful to hear about them.
Tibor @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_pycollect_makeitem(self, collector, name, obj): makeitem_result = yield items = makeitem_result.get_result() if items: try: self.raw_nodeids.extend([item.nodeid for item in items if isinstance(item, pytest.Item)]) except TypeError: # 'Class' object is not iterable pass The whole test script: import pytest pytest_plugins = "pytester" def test_pytest_assumption(testdir): testdir.makepyfile(__init__="") testdir.makepyfile(test_a=""" import pytest class Test1(): @pytest.mark.parametrize('a', [1, 2]) def test_1(self, a): pass def test_2(self): pass """) class Plugin: def __init__(self): self.raw_nodeids = [] @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_pycollect_makeitem(self, collector, name, obj): makeitem_result = yield items = makeitem_result.get_result() if items: try: self.raw_nodeids.extend([item.nodeid for item in items if isinstance(item, pytest.Item)]) except TypeError: # 'Class' object is not iterable pass plugin = Plugin() result = testdir.runpytest_inprocess("test_a.py::Test1::test_2", plugins=[plugin]) assert plugin.raw_nodeids == ['test_a.py::Test1::test_1[1]', 'test_a.py::Test1::test_1[2]', 'test_a.py::Test1::test_2'] result.assert_outcomes(1, 0, 0) On Sun, Nov 3, 2019 at 12:35 AM Tibor Arpas <tibor.ar...@infinit.sk> wrote: > So my method doesn't work when Packages are involved. package.collect() > returns the necessary nodes only once (it thinks the second execution > encountered duplicates, so it returns empty tuple). So my gen_nodeids > breaks normal collection. The bug or feature is demonstrated below. Is > there a better way, please? If I invoke runpytest with "--keepduplicates" > the test passes. Maybe I could use package._collectfile(..., > handle_dupes=False) but that is non-API territory so I didn't investigate > further yet. > > Tibor > > This fails with > /Users/tibor/tmonworkspace/testmon.io/test/test_collect.py:33: in > pytest_collect_file > assert nodeids == nodeids2 > E AssertionError: assert ['test_a.py::Test1::test_1', > 'test_a.py::Test1::test_2'] == [] > > import pytest > > pytest_plugins = "pytester" > > > def test_pytest_assumption(testdir): > testdir.makepyfile(__init__="") > > testdir.makepyfile(test_a=""" > class Test1(): > def test_1(self): > pass > def test_2(self): > pass > """) > > class Plugin: > > @pytest.hookimpl(hookwrapper=True) > def pytest_collect_file(self, path, parent): > def gen_nodeids(nodes): > for node in nodes: > if isinstance(node, pytest.Item): > yield node.nodeid > else: > yield from gen_nodeids(node.collect()) > > > collect_file_result = yield > nodeids = list(gen_nodeids(collect_file_result.get_result())) > nodeids2 = list(gen_nodeids(collect_file_result.get_result())) > assert len(nodeids) == 2 > assert nodeids == nodeids2 > > result = testdir.runpytest_inprocess("-v", "test_a.py::Test1::test_1", > plugins=[Plugin()]) > result.assert_outcomes(1, 0, 0) > > > > On Sat, Nov 2, 2019 at 9:52 PM Tibor Arpas <tibor.ar...@infinit.sk> wrote: > >> Hi, >> >> For pytest-testmon plugin I need to extract all node ids which exist in a >> python file (after parametrization). Initially, I thought >> pytest_collection_modifyitems used as a hookwrapper would have all the >> node_ids in all the files (which would be ideal for me). It turns out if >> you call pytest test_a.py::test_1 , pytest_collection_modifyitems only gets >> test_a.py::test_1 (but no other nodes which might be in test_a.py). >> pytest_pycollect_makemodule sounds like the pretty low-level hook which >> should get all I need. However, it gets a hierarchy, which I have to >> manually flatten. My gen_nodeids bellow looks like a code that definitely >> lives somewhere in pytest. What is the optimal and most forward-compatible >> way to get the list, please? Below is what I came up with so far (and it >> seems to work). >> >> I would be glad to get any comments or pointers. >> >> Best, >> Tibor >> twitter: tibor_a >> >> import pytest >> >> pytest_plugins = "pytester" >> >> >> def test_pytest_assumption(testdir): >> testdir.makepyfile(test_a=""" >> class Test1(): >> def test_1(self): >> pass >> """) >> >> class Plugin: >> @pytest.hookimpl(hookwrapper=True) >> def pytest_pycollect_makemodule(self, path, parent): >> def gen_nodeids(nodes): >> for node in nodes: >> if isinstance(node, pytest.Function): >> yield node.nodeid >> else: >> yield from gen_nodeids(node.collect()) >> >> make_module_result = yield >> nodeids = >> list(gen_nodeids(make_module_result.get_result().collect())) >> assert nodeids == ['test_a.py::Test1::test_1'] >> >> result = testdir.runpytest_inprocess("--testmon-dev", plugins=[Plugin()]) >> result.assert_outcomes(1, 0, 0) >> >>
_______________________________________________ pytest-dev mailing list pytest-dev@python.org https://mail.python.org/mailman/listinfo/pytest-dev