diff --git a/AUTHORS b/AUTHORS index 641e20441..7206125c8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -71,3 +71,4 @@ Eric Hunsberger Simon Gomizelj Russel Winder Ben Webb +Alexei Kozlenok diff --git a/CHANGELOG b/CHANGELOG index d1ac12a52..499660fa6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ 2.8.5.dev0 ---------- +- fix #1243: fixed injection to class breaking collection step, by replacing + generated interation with list traversal, added test to verify in python/collect.py + PR by Alexei Kozlenok, Thanks Ronny Pfannschmidt and Nicoddemus for the review and help. + - fix #1074: precompute junitxml chunks instead of storing the whole tree in objects Thanks Bruno Oliveira for the report and Ronny Pfannschmidt for the PR diff --git a/_pytest/python.py b/_pytest/python.py index 4ff3f4fc0..926f503be 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -451,7 +451,7 @@ class PyCollector(PyobjMixin, pytest.Collector): seen = {} l = [] for dic in dicts: - for name, obj in dic.items(): + for name, obj in list(dic.items()): if name in seen: continue seen[name] = True diff --git a/testing/python/collect.py b/testing/python/collect.py index 1a85faf9c..bebc13318 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1076,3 +1076,26 @@ def test_dont_collect_non_function_callable(testdir): 'WC2 *', '*1 passed, 1 pytest-warnings in *', ]) + + +def test_class_injection_does_not_break_collection(testdir): + """Tests whether injection during collection time will terminate testing. + + In this case the error should not occur if the TestClass itself + is modified during collection time, and the original method list + is still used for collection. + """ + testdir.makeconftest(""" + from test_inject import TestClass + def pytest_generate_tests(metafunc): + TestClass.changed_var = {} + """) + testdir.makepyfile(test_inject=''' + class TestClass(object): + def test_injection(self): + """Test being parametrized.""" + pass + ''') + result = testdir.runpytest() + assert "RuntimeError: dictionary changed size during iteration" not in result.stdout.str() + result.stdout.fnmatch_lines(['*1 passed*'])