11# -*- coding: utf-8 -*-
2- import os
32import sys
43import types
54from collections import defaultdict
6- import py
75import pytest
86from _pytest .fixtures import scopenum_function
97
@@ -43,13 +41,28 @@ def fill(request):
4341 return fill
4442
4543
44+ @pytest .hookimpl (tryfirst = True )
45+ def pytest_fixture_setup (fixturedef , request ):
46+ val = getattr (request , 'param' , None )
47+ if is_lazy_fixture (val ):
48+ request .param = request .getfixturevalue (val .name )
49+
50+
4651def pytest_runtest_call (item ):
4752 if hasattr (item , 'funcargs' ):
4853 for arg , val in item .funcargs .items ():
4954 if is_lazy_fixture (val ):
5055 item .funcargs [arg ] = item ._request .getfixturevalue (val .name )
5156
5257
58+ @pytest .hookimpl (hookwrapper = True )
59+ def pytest_pycollect_makeitem (collector , name , obj ):
60+ global current_node
61+ current_node = collector
62+ yield
63+ current_node = None
64+
65+
5366@pytest .hookimpl (hookwrapper = True )
5467def pytest_generate_tests (metafunc ):
5568 yield
@@ -66,40 +79,57 @@ def normalize_metafunc_calls(metafunc, valtype):
6679 metafunc ._calls = newcalls
6780
6881
82+ def parametrize_callspecs (callspecs , metafunc , fname , fparams ):
83+ allnewcallspecs = []
84+ for i , param in enumerate (fparams ):
85+ try :
86+ newcallspecs = [call .copy () for call in callspecs ]
87+ except TypeError :
88+ # pytest < 3.6.3
89+ newcallspecs = [call .copy (metafunc ) for call in callspecs ]
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+ for newcallspec in newcallspecs :
99+ newcallspec .setmulti2 (* setmulti_args )
100+ except AttributeError :
101+ # pytest < 3.3.0
102+ for newcallspec in newcallspecs :
103+ newcallspec .setmulti (* setmulti_args )
104+ allnewcallspecs .extend (newcallspecs )
105+ return allnewcallspecs
106+
107+
69108def normalize_call (callspec , metafunc , valtype , used_keys = None ):
70109 fm = metafunc .config .pluginmanager .get_plugin ('funcmanage' )
71- config = metafunc .config
72110
73111 used_keys = used_keys or set ()
74112 valtype_keys = set (getattr (callspec , valtype ).keys ()) - used_keys
75113
76- newcalls = []
77114 for arg in valtype_keys :
78115 val = getattr (callspec , valtype )[arg ]
79116 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-
117+ try :
118+ _ , fixturenames_closure , arg2fixturedefs = fm .getfixtureclosure ([val .name ], metafunc .definition .parent )
119+ except AttributeError :
120+ # pytest < 3.10.0
121+ fixturenames_closure , arg2fixturedefs = fm .getfixtureclosure ([val .name ], current_node )
122+
123+ extra_fixture_params = [(fname , arg2fixturedefs [fname ][- 1 ].params )
124+ for fname in fixturenames_closure if fname not in callspec .params
125+ if arg2fixturedefs .get (fname ) and arg2fixturedefs [fname ][- 1 ].params ]
126+
127+ if extra_fixture_params :
128+ newcallspecs = [callspec ]
129+ for fname , fparams in extra_fixture_params :
130+ newcallspecs = parametrize_callspecs (newcallspecs , metafunc , fname , fparams )
131+ newcalls = []
132+ for newcallspec in newcallspecs :
103133 calls = normalize_call (newcallspec , metafunc , valtype , used_keys | set ([arg ]))
104134 newcalls .extend (calls )
105135 return newcalls
@@ -151,14 +181,6 @@ def _tree_to_list(trees, leave):
151181 return lst
152182
153183
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-
162184def lazy_fixture (names ):
163185 if isinstance (names , string_type ):
164186 return LazyFixture (names )
0 commit comments