-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Fix nondeterminism in fixture collection order #2617
Fix nondeterminism in fixture collection order #2617
Conversation
This will probably break on Python 2.6, as |
Thanks for the PR! I'm not sure how to easily fix the About the test, I suggest to use the |
@nicoddemus this test will always have to use subprocesses as hash randomizarion is on a per process basis |
I am about to push updates with test. |
Added a test, and required ordereddict from pypi for py2.6. Although the test I posted in the original comment reliably fails with different values of PYTHONHASHSEED, it does not reliably fail when run as a subprocess from the full pytest test suite (when providing the same PYTHONHASHSEED variation to the subprocesses). I cannot understand how this might happen. |
I'm okay with adding the dependency, but that should definitely be noted in the changelog, and the PR should go to the |
Added new dependency for py26 in both changelog and docs/getting-started.rst. Retargeted to features rather than master. Is there a policy on python2.6 support? I note that the last ever bug-fix release was October 2013. |
I'm fine with depending on
We plan to drop it as soon as pip does in pip 10.0, see #1273. |
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.
Thanks a lot!
I commented on some small nitpicks below.
_pytest/fixtures.py
Outdated
@@ -1,6 +1,12 @@ | |||
from __future__ import absolute_import, division, print_function | |||
import sys | |||
|
|||
try: | |||
from collections import OrderedDict |
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.
Please check the version explicitly instead of relying on ImportError
:
if sys.version_info[:2] == (2, 6):
from ordereddict import OrderedDict
else:
from collections import OrderedDict
Using ImportError
for that may lead to nasty surprises (see pytest-dev/pytest-mock#68 for a detailed example), plus this will make it easier for us later to find it and remove it when we drop 2.6 support.
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.
Done. I guess it could go into _pytest/compat.py
instead of here?
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.
Done, I guess this import could go into _pytest/compat.py
rather than here?
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.
Good idea, this could be:
from _pytest.compat import OrderedDict
👍
testing/python/fixture.py
Outdated
@@ -2547,6 +2548,44 @@ def test_foo(fix): | |||
'*test_foo*alpha*', | |||
'*test_foo*beta*']) | |||
|
|||
@pytest.mark.issue920 | |||
def test_deterministic_fixture_collection(self, testdir): |
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.
Please use the monkeypatch
fixture instead of importing and creating your own MonkeyPatch
instance.
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.
Done.
testing/python/fixture.py
Outdated
monkeypatch = MonkeyPatch() | ||
monkeypatch.setenv("PYTHONHASHSEED", "1") | ||
out1 = testdir.runpytest_subprocess("-v") | ||
monkeypatch.undo() |
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.
No need to undo here
testing/python/fixture.py
Outdated
monkeypatch.undo() | ||
monkeypatch.setenv("PYTHONHASHSEED", "2") | ||
out2 = testdir.runpytest_subprocess("-v") | ||
monkeypatch.undo() |
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.
Nor here, the monkeypatch
fixture ensures to undo itself when the test ends.
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.
Thanks, done.
testing/python/fixture.py
Outdated
@@ -2547,6 +2548,44 @@ def test_foo(fix): | |||
'*test_foo*alpha*', | |||
'*test_foo*beta*']) | |||
|
|||
@pytest.mark.issue920 | |||
def test_deterministic_fixture_collection(self, testdir): | |||
a = testdir.mkdir("a") |
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.
Not sure you need the sub directory, plus you can ask testdir
to create the file for you:
testdir.makepyfile(''''
import pytest
@pytest.fixture(scope="module",
...
This will create a test file named test_deterministic_fixture_collection.py
(same as the test name) in the test directory.
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.
Ah, I see, I was cargo-culting from the wrong place.
fixtures.reorder_items is non-deterministic because it reorders based on iteration over an (unordered) set. Change the code to use an OrderedDict instead so that we get deterministic behaviour, fixes #920.
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.
Awesome thanks @wence- !
That last bit about _pytest.compat
is up to you and minor, it can be merged as soon as CI passes IMO.
I'm happy to leave as is. |
fixtures.reorder_items is non-deterministic because it reorders based
on iteration over an (unordered) set. Change the code to use an
OrderedDict instead, so that we get deterministic behaviour.
The xdist issue in #920 is a red-herring. Fixture collection is non-deterministic across pytest invocations full stop if hash randomisation is on.
For example, consider the following test file:
Note how successive incantations of pytest arrive at different orders of the collected tests.
The problem lies in
fixtures.reorder_items
which reorders (in part) based on iteration over an unordered set. Fix this by switching to using anOrderedDict
.It's not obvious to me how to write a good test for this.
Thanks for submitting a PR, your contribution is really appreciated!
Here's a quick checklist that should be present in PRs:
$issue_id.$type
for example (588.bug)removal
,feature
,bugfix
,vendor
,doc
ortrivial
bugfix
,vendor
,doc
ortrivial
fixes, targetmaster
; for removals or features targetfeatures
;Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:
AUTHORS
;