def parametrize(metafunc, argnames, argvalues, indirect=False, ids=None, scope=None, **kwargs): """ This alternate implementation of metafunc.parametrize creates a list of calls that is not just the cartesian product of all parameters (like the pytest behaviour). Instead, it offers an alternate list of calls takinginto account all union fixtures. For this, it replaces the `metafunc._calls` attribute with a `CallsReactor` instance, and feeds it with all parameters and parametrized fixtures independently (not doing any cross-product). The resulting `CallsReactor` instance is then able to dynamically behave like the correct list of calls, lazy-creating that list when it is used. """ # create our special container object if needed if not isinstance(metafunc._calls, CallsReactor): # first call: should be an empty list if len(metafunc._calls) > 0: raise ValueError("This should not happen - please file an issue") metafunc._calls = CallsReactor(metafunc) # grab it calls_reactor = metafunc._calls # detect union fixtures if is_fixture_union_params(argvalues): if ',' in argnames or not isinstance(argnames, string_types): raise ValueError("Union fixtures can not be parametrized") union_fixture_name = argnames union_fixture_alternatives = argvalues if indirect is False or len(kwargs) > 0: raise ValueError( "indirect cannot be set on a union fixture, as well as unknown kwargs" ) # add a union parametrization in the queue (but do not apply it now) calls_reactor.append( UnionParamz(union_fixture_name, union_fixture_alternatives, ids, scope, kwargs)) else: # add a normal parametrization in the queue (but do not apply it now) calls_reactor.append( NormalParamz(argnames, argvalues, indirect, ids, scope, kwargs))
def _build_closure( self, fixture_defs_mgr, # type: FixtureDefsCache initial_fixture_names # type: Iterable[str] ): """ :param arg2fixturedefs: set of fixtures already known by the parent node :return: nothing (the input arg2fixturedefs is modified) """ # Grab all dependencies of all fixtures present at this node and add them to either this or to nodes below. # -- first switch this object from 'pending' to 'under construction' if needed # (indeed we now authorize and use the possibility to call this twice. see split() ) if self.fixture_defs is None: self.fixture_defs = OrderedDict() # -- then for all pending, add them with their dependencies pending_fixture_names = list(initial_fixture_names) while len(pending_fixture_names) > 0: fixname = pending_fixture_names.pop(0) # if the fixture is already known in this node or above, do not care if self.already_knows_fixture(fixname): continue # else grab the fixture definition(s) for this fixture name for this test node id fixturedefs = fixture_defs_mgr.get_fixture_defs(fixname) if not fixturedefs: # fixture without definition: add it self.add_required_fixture(fixname, None) else: # the actual definition is the last one _fixdef = fixturedefs[-1] _params = _fixdef.params if _params is not None and is_fixture_union_params(_params): # create an UNION fixture # transform the _params into a list of names alternative_f_names = UnionFixtureAlternative.to_list_of_fixture_names( _params) # if there are direct dependencies that are not the union members, add them to pending non_member_dependencies = [ f for f in _fixdef.argnames if f not in alternative_f_names ] pending_fixture_names += non_member_dependencies # propagate WITH the pending self.split_and_build(fixture_defs_mgr, fixname, fixturedefs, alternative_f_names, pending_fixture_names) # empty the pending pending_fixture_names = [] else: # normal fixture self.add_required_fixture(fixname, fixturedefs) # add all dependencies in the to do list dependencies = _fixdef.argnames # - append: was pytest default # pending_fixture_names += dependencies # - prepend: makes much more sense pending_fixture_names = list( dependencies) + pending_fixture_names