Skip to content

Commit aed9949

Browse files
committed
Adding support for parametrized subfixtures of lazily-used fixtures (making test_lazy_fixtures_with_subfixtures succeed)
1 parent bc21f4c commit aed9949

File tree

1 file changed

+50
-35
lines changed

1 file changed

+50
-35
lines changed

pytest_lazyfixture.py

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# -*- coding: utf-8 -*-
2-
import os
32
import sys
43
import types
54
from collections import defaultdict
6-
import py
75
import pytest
86
from _pytest.fixtures import scopenum_function
97

@@ -50,6 +48,14 @@ def pytest_runtest_call(item):
5048
item.funcargs[arg] = item._request.getfixturevalue(val.name)
5149

5250

51+
@pytest.hookimpl(hookwrapper=True)
52+
def pytest_pycollect_makeitem(collector, name, obj):
53+
global current_node
54+
current_node = collector
55+
yield
56+
current_node = None
57+
58+
5359
@pytest.hookimpl(hookwrapper=True)
5460
def pytest_generate_tests(metafunc):
5561
yield
@@ -66,40 +72,57 @@ def normalize_metafunc_calls(metafunc, valtype):
6672
metafunc._calls = newcalls
6773

6874

75+
def parametrize_callspecs(callspecs, metafunc, fname, fparams):
76+
allnewcallspecs = []
77+
for i, param in enumerate(fparams):
78+
try:
79+
newcallspecs = [call.copy() for call in callspecs]
80+
except TypeError:
81+
# pytest < 3.6.3
82+
newcallspecs = [call.copy(metafunc) for call in callspecs]
83+
84+
# TODO: for now it uses only function scope
85+
# TODO: idlist
86+
setmulti_args = (
87+
{fname: 'params'}, (fname,), (param,),
88+
None, (), scopenum_function, i
89+
)
90+
try:
91+
for newcallspec in newcallspecs:
92+
newcallspec.setmulti2(*setmulti_args)
93+
except AttributeError:
94+
# pytest < 3.3.0
95+
for newcallspec in newcallspecs:
96+
newcallspec.setmulti(*setmulti_args)
97+
allnewcallspecs.extend(newcallspecs)
98+
return allnewcallspecs
99+
100+
69101
def normalize_call(callspec, metafunc, valtype, used_keys=None):
70102
fm = metafunc.config.pluginmanager.get_plugin('funcmanage')
71-
config = metafunc.config
72103

73104
used_keys = used_keys or set()
74105
valtype_keys = set(getattr(callspec, valtype).keys()) - used_keys
75106

76-
newcalls = []
77107
for arg in valtype_keys:
78108
val = getattr(callspec, valtype)[arg]
79109
if is_lazy_fixture(val):
80-
fname = val.name
81-
nodeid = get_nodeid(metafunc.module, config.rootdir)
82-
fdef = fm.getfixturedefs(fname, nodeid)
83-
if fname not in callspec.params and fdef and fdef[-1].params:
84-
for i, param in enumerate(fdef[0].params):
85-
try:
86-
newcallspec = callspec.copy()
87-
except TypeError:
88-
# pytest < 3.6.3
89-
newcallspec = callspec.copy(metafunc)
90-
91-
# TODO: for now it uses only function scope
92-
# TODO: idlist
93-
setmulti_args = (
94-
{fname: 'params'}, (fname,), (param,),
95-
None, (), scopenum_function, i
96-
)
97-
try:
98-
newcallspec.setmulti2(*setmulti_args)
99-
except AttributeError:
100-
# pytest < 3.3.0
101-
newcallspec.setmulti(*setmulti_args)
102-
110+
try:
111+
_, fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], metafunc.definition.parent)
112+
except AttributeError:
113+
# pytest < 3.10.0
114+
fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([val.name], current_node)
115+
116+
extra_fixture_params = [(fname, arg2fixturedefs[fname][0].params)
117+
for fname in fixturenames_closure if fname not in callspec.params
118+
if arg2fixturedefs.get(fname) and arg2fixturedefs[fname][0].params]
119+
120+
if extra_fixture_params:
121+
newcallspecs = [callspec]
122+
for fname, fparams in extra_fixture_params:
123+
newcallspecs = parametrize_callspecs(newcallspecs, metafunc, fname, fparams)
124+
newcalls = []
125+
for newcallspec in newcallspecs:
103126
calls = normalize_call(newcallspec, metafunc, valtype, used_keys | set([arg]))
104127
newcalls.extend(calls)
105128
return newcalls
@@ -151,14 +174,6 @@ def _tree_to_list(trees, leave):
151174
return lst
152175

153176

154-
def get_nodeid(module, rootdir):
155-
path = py.path.local(module.__file__)
156-
relpath = path.relto(rootdir)
157-
if os.sep != "/":
158-
relpath = relpath.replace(os.sep, "/")
159-
return relpath
160-
161-
162177
def lazy_fixture(names):
163178
if isinstance(names, string_type):
164179
return LazyFixture(names)

0 commit comments

Comments
 (0)