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

Reply via email to