-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Account for changes in fixture dependencies properly #14104
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
base: main
Are you sure you want to change the base?
Conversation
|
@RonnyPfannschmidt please review 😃 |
RonnyPfannschmidt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm currently not able to do a deep review
This has a few details that give me headaches
- it sidesteps setup/teardown minimizing and im not sure if its feasible to integrate in a comprehensible manner
- its unclear to me what happens if the involved fixtures are indirect to a test
- Its unclear to me what happens if someone creates a cycle via the new dependency tracking
Are you wondering if fixing #14095 is an overall positive change? If so, I can remove that part from the PR for now, and it can be investigated later separately. There is also a minor issue with my implementation. When I do request._execute_fixturedef(fixturedef, only_maintain_cache=True)it may I can change it so that the other fixtures are not
What do you mean? Parametrized / non-parametrized case? In the parametrized case, we cannot get the parametrized fixture using
The initial cache handling step uses There is |
|
trigger a rebuild is more correct than not with indirect i mean what happens if a replaced fixture is not a dependency of the directly used fixtures, but rather down in the dependency tree with loops i mean what actually happens when one fixture goads another ones closure into calling getfixturevalue for itself |
|
I've found another issue in my implementation. Suppose we depend on some fixture dynamically. After a recomputation we stop depending on it. Everything is fine, but at some point that fixture gets finalized, and because we did One solution is to add "evaluation epoch" to the finalizers to avoid finalizing a more recent evaluation of the fixture than expected. |
|
teardown should idieally only happen in teardown phases - thats why the teardown_towards heleprs got added we should fail rather than teardown in setup/call phases |
|
I've moved parametrized fixture finalization to teardown phase. In We need to be cautious with what to teardown when. Consider these tests: @pytest.fixture(scope="session")
def foo(request):
return request.param
@pytest.mark.parametrize("foo", [1], indirect=True)
def test_a(foo):
pass
@pytest.mark.parametrize("foo", [1], indirect=True)
def test_b(foo):
pass
def test_c():
pass
@pytest.mark.parametrize("foo", [2], indirect=True)
def test_d(foo):
passFinalizing For each parametrized fixture in the current item we should find the next item that uses the fixture with that parameter and if parameters differ then finalize the fixture in the current item's teardown. This requires some code restructuring, because we now require arbitrary lookahead instead of just Overall, setup-plan did get prettier. A hefty chunk of I need to write more tests, for issues you pointed out and some more:
|
25fab5f to
643afdf
Compare
|
I've reworked More importantly, this disallows
The implementations of Alternatively, we could avoid all of those troubles by dropping support for carrying fixtures along tests that do not depend on them: @pytest.fixture(scope="session")
def foo(request):
return request.param
@pytest.parametrize("foo", [1], indirect=True)
def test_a(foo):
pass
def test_b():
assert 2 + 2 == 4
@pytest.parametrize("foo", [1], indirect=True)
def test_c(foo):
passIn this case, pytest auto-reorders fixtures based on param values (if reordering is not turned off by the user), but in a more complex scenario additional teardown-setup work will surface: @pytest.fixture(scope="session")
def foo():
pass
@pytest.fixture(scope="session")
def bar():
pass
@pytest.fixture(scope="session")
def baz():
pass
@pytest.parametrize(("foo", "bar"), [1, 1], indirect=True)
def test_a(foo, bar):
pass
@pytest.parametrize(("bar, "baz"), [1, 1], indirect=True)
def test_b(bar, baz):
pass
@pytest.parametrize(("baz, "foo"), [1, 1], indirect=True)
def test_c(bar, baz):
passCurrently all those fixtures are carried over. They will not be carried over. And so I come back to the "disgusting" idea of tearing down parametrized fixtures in the test before the parameter changes. So in this test setup, |
|
Done! I should add the following tests (besides the ones mentioned earlier in one, two):
|
|
By the way, this PR also relates to:
|
Closes: #14095
Closes: #14103