Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def one_fixture_per_step_decorate(fixture_fun):
""" Implementation of the @one_fixture_per_step decorator, for manual decoration"""
def _check_scope(request):
scope = get_scope(request)
if scope != 'function':
# session- or module-scope
raise Exception("The `@one_fixture_per_step` decorator is only useful for function-scope fixtures. `%s`"
" seems to have scope='%s'. Consider removing `@one_fixture_per_step` or changing "
"the scope to 'function'." % (fixture_fun, scope))
# We will expose a new signature with additional arguments
orig_sig = signature(fixture_fun)
func_needs_request = 'request' in orig_sig.parameters
if not func_needs_request:
new_sig = add_signature_parameters(orig_sig, first=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD))
else:
new_sig = orig_sig
if not isgeneratorfunction(fixture_fun):
@wraps(fixture_fun, new_sig=new_sig)
def _steps_aware_decorated_function(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
_check_scope(request)
res = fixture_fun(*args, **kwargs)
return _OnePerStepFixtureProxy(res)
else:
@wraps(fixture_fun, new_sig=new_sig)
def _steps_aware_decorated_function(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
_check_scope(request)
gen = fixture_fun(*args, **kwargs)
# note: the function automatically registers it in the module
# note 2: idstyle is set to None because we provide an explicit enough list of ids
big_param_fixture = _fixture_union(caller_module, fixture_union_name, fixtures_to_union, idstyle=None,
ids=fixtures_to_union_names_for_ids, hook=hook)
# --create the new test function's signature that we want to expose to pytest
# it is the same than existing, except that we want to replace all parameters with the new fixture
# first check where we should insert the new parameters (where is the first param we remove)
for _first_idx, _n in enumerate(old_sig.parameters):
if _n in all_param_names:
break
# then remove all parameters that will be replaced by the new fixture
new_sig = remove_signature_parameters(old_sig, *all_param_names)
# finally insert the new fixture in that position. Indeed we can not insert first or last, because
# 'self' arg (case of test class methods) should stay first and exec order should be preserved when possible
new_sig = add_signature_parameters(new_sig, custom_idx=_first_idx,
custom=Parameter(fixture_union_name, kind=Parameter.POSITIONAL_OR_KEYWORD))
# --Finally create the fixture function, a wrapper of user-provided fixture with the new signature
def replace_paramfixture_with_values(kwargs):
# remove the created fixture value
encompassing_fixture = kwargs.pop(fixture_union_name)
# and add instead the parameter values
if nb_params > 1:
for i, p in enumerate(all_param_names):
kwargs[p] = encompassing_fixture[i]
else:
kwargs[all_param_names[0]] = encompassing_fixture
# return
return kwargs
if not isgeneratorfunction(test_func):
# Transform the steps into ids if needed
step_ids = [create_pytest_param_str_id(f) for f in steps]
# Create the container that will hold all execution monitors for this function
# TODO maybe have later a single 'monitor' instance at plugin level... like in pytest-benchmark
all_monitors = StepMonitorsContainer(test_func, step_ids)
# Create the function wrapper.
# We will expose a new signature with additional 'request' arguments if needed, and the test step
orig_sig = signature(test_func)
func_needs_request = 'request' in orig_sig.parameters
additional_params = (Parameter(test_step_argname, kind=Parameter.POSITIONAL_OR_KEYWORD), ) \
+ ((Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD), )
if not func_needs_request else ())
# add request parameter last, as first may be 'self'
new_sig = add_signature_parameters(orig_sig, last=additional_params)
# -- first create the logic
@wraps(test_func, new_sig=new_sig)
def wrapped_test_function(*args, **kwargs):
step_name = kwargs.pop(test_step_argname)
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
if request is None:
# we are manually called outside of pytest. let's execute all steps at nce
if step_name is None:
# print("@test_steps - decorated function '%s' is being called manually. The `%s` parameter is set "
# "to None so all steps will be executed in order" % (f, test_step_argname))
step_names = step_ids
else:
# print("@test_steps - decorated function '%s' is being called manually. The `%s` parameter is set "
# "to %s so only these steps will be executed in order. Note that the order should be feasible"
# "" % (f, test_step_argname, step_name))
raise ValueError("Internal error related to fixture parametrization- please report")
# (4) wrap the fixture function so as to remove the parameter names and add 'request' if needed
all_param_names = tuple(v for l in params_names_or_name_combinations for v in l)
# --create the new signature that we want to expose to pytest
old_sig = signature(fixture_func)
for p in all_param_names:
if p not in old_sig.parameters:
raise ValueError("parameter '%s' not found in fixture signature '%s%s'"
"" % (p, fixture_func.__name__, old_sig))
new_sig = remove_signature_parameters(old_sig, *all_param_names)
# add request if needed
func_needs_request = 'request' in old_sig.parameters
if not func_needs_request:
new_sig = add_signature_parameters(new_sig, first=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD))
# --common routine used below. Fills kwargs with the appropriate names and values from fixture_params
def _get_arguments(*args, **kwargs):
request = kwargs['request'] if func_needs_request else kwargs.pop('request')
# populate the parameters
if len(params_names_or_name_combinations) == 1:
_params = [request.param] # remove the simplification
else:
_params = request.param
for p_names, fixture_param_value in zip(params_names_or_name_combinations, _params):
if len(p_names) == 1:
# a single parameter for that generated fixture (@pytest.mark.parametrize with a single name)
kwargs[p_names[0]] = fixture_param_value
else:
# several parameters for that generated fixture (@pytest.mark.parametrize with several names)
Implementation of the @cross_steps_fixture decorator, for manual decoration
:param fixture_fun:
:param step_param_names: a singleton or iterable containing the names of the test step parameters used in the
tests. By default the list is `[GENERATOR_MODE_STEP_ARGNAME, TEST_STEP_ARGNAME_DEFAULT]` to cover both
generator-mode and legacy manual mode.
:return:
"""
ref_dct = dict()
# Create the function wrapper.
# We will expose a new signature with additional 'request' arguments if needed, and the test step
orig_sig = signature(fixture_fun)
func_needs_request = 'request' in orig_sig.parameters
if not func_needs_request:
new_sig = add_signature_parameters(orig_sig, first=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD))
else:
new_sig = orig_sig
def _init_and_check(request):
"""
Checks that the current request is not session but a specific node.
:param request:
:return:
"""
scope = get_scope(request)
if scope == 'function':
# function-scope: ok
id_without_steps = get_pytest_node_hash_id(request.node,
params_to_ignore=_get_step_param_names_or_default(
step_param_names))
return id_without_steps
raise ValueError("Internal error related to fixture parametrization- please report")
# (4) wrap the fixture function so as to remove the parameter names and add 'request' if needed
all_param_names = tuple(v for pnames in params_names_or_name_combinations for v in pnames)
# --create the new signature that we want to expose to pytest
old_sig = signature(fixture_func)
for p in all_param_names:
if p not in old_sig.parameters:
raise ValueError("parameter '%s' not found in fixture signature '%s%s'"
"" % (p, fixture_func.__name__, old_sig))
new_sig = remove_signature_parameters(old_sig, *all_param_names)
# add request if needed
func_needs_request = 'request' in old_sig.parameters
if not func_needs_request:
new_sig = add_signature_parameters(new_sig, first=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD))
# --common routine used below. Fills kwargs with the appropriate names and values from fixture_params
def _map_arguments(*_args, **_kwargs):
request = _kwargs['request'] if func_needs_request else _kwargs.pop('request')
# populate the parameters
if len(params_names_or_name_combinations) == 1:
_params = [request.param] # remove the simplification
else:
_params = request.param
for p_names, fixture_param_value in zip(params_names_or_name_combinations, _params):
if len(p_names) == 1:
# a single parameter for that generated fixture (@pytest.mark.parametrize with a single name)
_kwargs[p_names[0]] = get_lazy_args(fixture_param_value)
else:
# several parameters for that generated fixture (@pytest.mark.parametrize with several names)
_make_fixture_union(caller_module, name=fixture_union_name, hook=hook, caller=parametrize_plus,
fix_alternatives=fix_alternatives, unique_fix_alt_names=fix_alt_names,
ids=explicit_ids_to_use or ids or ParamIdMakers.get(idstyle))
# --create the new test function's signature that we want to expose to pytest
# it is the same than existing, except that we want to replace all parameters with the new fixture
# first check where we should insert the new parameters (where is the first param we remove)
_first_idx = -1
for _first_idx, _n in enumerate(old_sig.parameters):
if _n in argnames:
break
# then remove all parameters that will be replaced by the new fixture
new_sig = remove_signature_parameters(old_sig, *argnames)
# finally insert the new fixture in that position. Indeed we can not insert first or last, because
# 'self' arg (case of test class methods) should stay first and exec order should be preserved when possible
new_sig = add_signature_parameters(new_sig, custom_idx=_first_idx,
custom=Parameter(fixture_union_name,
kind=Parameter.POSITIONAL_OR_KEYWORD))
if debug:
print("Creating final test function wrapper with signature %s%s" % (test_func_name, new_sig))
# --Finally create the fixture function, a wrapper of user-provided fixture with the new signature
def replace_paramfixture_with_values(kwargs): # noqa
# remove the created fixture value
encompassing_fixture = kwargs.pop(fixture_union_name)
# and add instead the parameter values
if nb_params > 1:
for i, p in enumerate(argnames): # noqa
kwargs[p] = encompassing_fixture[i]
else:
kwargs[argnames[0]] = encompassing_fixture
modulename=function_for_metadata.__module__)
def new_decorator(*no_args, **kwargs):
"""
Code for your decorator, generated by decopatch to handle the case when it is called without parenthesis
"""
# this is a parenthesis call, because otherwise a `TypeError` would already have been raised by python.
return with_parenthesis_usage(decorator_function, *no_args, **kwargs)
return new_decorator
elif sig_info.use_signature_trick:
# no need to modify the signature, we will expose *args, **kwargs
pass
else:
# modify the signature to add a var-positional first
gen_varpos_param = Parameter(_GENERATED_VARPOS_NAME, kind=Parameter.VAR_POSITIONAL)
sig_info.exposed_signature = add_signature_parameters(sig_info.exposed_signature, first=[gen_varpos_param])
# we can fallback to the same case than varpositional
return create_general_case_decorator(sig_info, decorator_function, disambiguator,
function_for_metadata=function_for_metadata)