Skip to content

Indirect session fixture reordering not working as expected #8914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nicoddemus opened this issue Jul 15, 2021 · 5 comments · May be fixed by #9420
Open

Indirect session fixture reordering not working as expected #8914

nicoddemus opened this issue Jul 15, 2021 · 5 comments · May be fixed by #9420
Labels
topic: fixtures anything involving fixtures directly or indirectly type: bug problem that needs to be addressed

Comments

@nicoddemus
Copy link
Member

nicoddemus commented Jul 15, 2021

There seems to be a problem with our reordering code that optimizes higher-level fixtures' execution order.

import pytest

@pytest.fixture(scope="session")
def prepare(request):
    print("\nprepare setup with {!r}".format(request.param))

@pytest.mark.parametrize('prepare', ["dina"], indirect=True, scope="session")
def test_1(prepare):
    print("Test1 done\n")

@pytest.mark.parametrize('prepare', ["mor"], indirect=True, scope="session")
def test_2(prepare):
    print("Test2 done\n")

@pytest.mark.parametrize('prepare', ["dina"], indirect=True, scope="session")
def test_3(prepare):
    print("Test3 done\n")

I get this output:

λ pytest .tmp\test_fix_session.py -sq

prepare setup with 'dina'
Test1 done

.
prepare setup with 'mor'
Test2 done

.
prepare setup with 'dina'
Test3 done

.
3 passed in 0.02s

We would expect it to reorder the tests to [test_1, test_3, test_2] so the fixture would execute only twice, instead of 3 times as it is doing now, so definitely looks like a bug (unless I'm missing something).

Thanks @dina809 for the original report (#8328).

Originally posted by @nicoddemus in #8328 (comment)

@nicoddemus nicoddemus added topic: fixtures anything involving fixtures directly or indirectly type: bug problem that needs to be addressed labels Jul 15, 2021
@nicoddemus nicoddemus changed the title Indirect session fixture / item reordering issue Indirect session fixture reordering issue not working as expected Jul 15, 2021
@nicoddemus nicoddemus changed the title Indirect session fixture reordering issue not working as expected Indirect session fixture reordering not working as expected Jul 15, 2021
@Formartha

This comment has been minimized.

@symonk
Copy link
Member

symonk commented Jul 31, 2021

I'm gonna have a look into this one next on my hit list, I've read the other thread for all the context and will start debugging soon

haxtibal pushed a commit to haxtibal/pytest that referenced this issue Nov 27, 2021
Reordering test items only took parameter indexes into account. This
works fine for direct parametrization. But for indirect parametrization,
the parameter index is always 0, and therefore every item went into the
same argkey group.

Now also add the parameter value to the argkey. This is enough
information to establish different argkey groups and make the grouping
algorithm work.

Fixes pytest-dev#8914.
@haxtibal
Copy link
Contributor

haxtibal commented Nov 27, 2021

@nicoddemus This issue hit me as well, hopefully I found the root cause and a possible fix, see this commit

Reordering test items only took parameter indexes into account. This
works fine for direct parametrization. But for indirect parametrization,
the parameter index is always 0, and therefore every item went into the
same argkey group.

Now also add the parameter value to the argkey. This is enough
information to establish different argkey groups and make the grouping
algorithm work.

The output from your minimal reproducer is now as expected

prepare setup with 'dina'
Test1 done
Test3 done
prepare setup with 'mor'
Test2 done

but the change makes 3 tests fail (test_parameters_without_eq_semantics, test_parameters_without_eq_semantics, test_parametrize_issue634). I've not yet looked at them in detail.

Could you please give some short feedback? If you think I'm on the right track, I'll try to understand and fix the regression and open a PR here.

@nicoddemus
Copy link
Member Author

Hi @haxtibal, that's great! I looked at the code and it makes sense.

The failing tests are about making sure that pytest works even if the argvalues can't compare with == (raise an error for example).

I think a solution is to return a wrapper instead of the actual object, something like:

@attr.s(autoattribs=True, eq=False)
class SafeEqWrapper:

    obj: object

    def __eq__(self, other: object) -> Any:
        try:
            res = self.obj == other
            bool(res)
            return res
        except Exception:
            # perhaps return False?
            return id(self.obj) == id(other)

And then use it like this:

key: _Key = (argname, param_index, SafeEqWrapper(param_value))

Feel free to open a PR even if incomplete, we can discuss this over the code. 👍

haxtibal pushed a commit to haxtibal/pytest that referenced this issue Nov 29, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer setup/teardown
cycles. Prior to this commit, grouping considered an parameter's index,
not the parameter value itself, to determine whether a parameter is "the
same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
using one index can no longer refer to "the same" parameter. In order to
still be able to group for fixture reuse, we have to group by parameter
value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Nov 29, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer fixture
setup/teardown cycles. Prior to this commit, grouping considered an
parameter's index, not the parameter value itself, to determine whether
a parameter is "the same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
one index can no longer refer to "the same" parameter. In order to still
be able to group for fixture reuse, we have to group by parameter value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Nov 29, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer fixture
setup/teardown cycles. Prior to this commit, grouping considered an
parameter's index, not the parameter value itself, to determine whether
a parameter is "the same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
one index can no longer refer to "the same" parameter. In order to still
be able to group for fixture reuse, we have to group by parameter value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Nov 29, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer fixture
setup/teardown cycles. Prior to this commit, grouping considered an
parameter's index, not the parameter value itself, to determine whether
a parameter is "the same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
one index can no longer refer to "the same" parameter. In order to still
be able to group for fixture reuse, we have to group by parameter value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
@haxtibal
Copy link
Contributor

Thank's for your suggestions @nicoddemus ! We can switch over to #9350 now.

haxtibal pushed a commit to haxtibal/pytest that referenced this issue Dec 9, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer fixture
setup/teardown cycles. Prior to this commit, grouping considered an
parameter's index, not the parameter value itself, to determine whether
a parameter is "the same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
one index can no longer refer to "the same" parameter. In order to still
be able to group for fixture reuse, we have to group by parameter value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Dec 16, 2021
Add a test to assert pytest-dev#8914 is fixed. The test assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Dec 18, 2021
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Dec 19, 2021
reorder_items groups tests so that each group can share indirectly
parameterized fixture instances. This optimizes for fewer fixture
setup/teardown cycles. Prior to this commit, grouping considered an
parameter's index, not the parameter value itself, to determine whether
a parameter is "the same" and therefore can be shared.

Relying on indexes is fast, however only works when there's exactly one
parameter list for one fixture. If we provide multiple (possibly
different) lists for the same fixture, e.g. by decorating test items,
one index can no longer refer to "the same" parameter. In order to still
be able to group for fixture reuse, we have to group by parameter value.

Caution: The value ends up inside the key of another dict, and therefore
must be hashable. This was true for indexes, but no longer is guaranteed
for user provided values. A user may e.g. provide dicts or numpy arrays.
The SafeHashWrapper ensures a fallback to id() in such a case.

Fixes pytest-dev#8914.
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Jan 23, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Jan 23, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Jan 23, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Jan 23, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
haxtibal pushed a commit to haxtibal/pytest that referenced this issue Jan 26, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
bluetech pushed a commit to haxtibal/pytest that referenced this issue Jan 27, 2022
Add tests to assert pytest-dev#8914 is fixed. Tests assures that both reordering
and caching work as intended, and demonstrates how one can use parameter
ids to decide about equality.

Adapting issue_519.checked_order is not cheating. See our discussion at
pytest-dev#9350 (review)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: fixtures anything involving fixtures directly or indirectly type: bug problem that needs to be addressed
Projects
None yet
4 participants