Skip to content

Commit 8ff10ee

Browse files
committed
Fix nondeterminism in fixture collection order
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.
1 parent 79097e8 commit 8ff10ee

File tree

3 files changed

+11
-8
lines changed

3 files changed

+11
-8
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Kale Kundert
9191
Katarzyna Jachim
9292
Kevin Cox
9393
Kodi B. Arfer
94+
Lawrence Mitchell
9495
Lee Kamentsky
9596
Lev Maximov
9697
Llandy Riveron Del Risco

_pytest/fixtures.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import absolute_import, division, print_function
22
import sys
33

4+
from collections import OrderedDict
45
from py._code.code import FormattedExcinfo
56

67
import py
@@ -136,10 +137,10 @@ def get_parametrized_fixture_keys(item, scopenum):
136137
except AttributeError:
137138
pass
138139
else:
139-
# cs.indictes.items() is random order of argnames but
140-
# then again different functions (items) can change order of
141-
# arguments so it doesn't matter much probably
142-
for argname, param_index in cs.indices.items():
140+
# cs.indices.items() is random order of argnames. Need to
141+
# sort this so that different calls to
142+
# get_parametrized_fixture_keys will be deterministic.
143+
for argname, param_index in sorted(cs.indices.items()):
143144
if cs._arg2scopenum[argname] != scopenum:
144145
continue
145146
if scopenum == 0: # session
@@ -161,7 +162,7 @@ def reorder_items(items):
161162
for scopenum in range(0, scopenum_function):
162163
argkeys_cache[scopenum] = d = {}
163164
for item in items:
164-
keys = set(get_parametrized_fixture_keys(item, scopenum))
165+
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
165166
if keys:
166167
d[item] = keys
167168
return reorder_items_atscope(items, set(), argkeys_cache, 0)
@@ -196,9 +197,9 @@ def slice_items(items, ignore, scoped_argkeys_cache):
196197
for i, item in enumerate(it):
197198
argkeys = scoped_argkeys_cache.get(item)
198199
if argkeys is not None:
199-
argkeys = argkeys.difference(ignore)
200-
if argkeys: # found a slicing key
201-
slicing_argkey = argkeys.pop()
200+
newargkeys = OrderedDict.fromkeys(k for k in argkeys if k not in ignore)
201+
if newargkeys: # found a slicing key
202+
slicing_argkey, _ = newargkeys.popitem()
202203
items_before = items[:i]
203204
items_same = [item]
204205
items_other = []

changelog/920.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix non-determinism in order of fixture collection.

0 commit comments

Comments
 (0)