Merge pull request #1535 from palaviv/parametrize-test-ids-hook
introduce pytest_make_parametrize_id hook
This commit is contained in:
		
						commit
						6cc56b4a1b
					
				| 
						 | 
					@ -27,7 +27,8 @@
 | 
				
			||||||
  whether to filter the traceback based on the ``ExceptionInfo`` object passed
 | 
					  whether to filter the traceback based on the ``ExceptionInfo`` object passed
 | 
				
			||||||
  to it.
 | 
					  to it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*
 | 
					* New ``pytest_make_parametrize_id`` hook.
 | 
				
			||||||
 | 
					  Thanks `@palaviv`_ for the PR.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Changes**
 | 
					**Changes**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,6 +156,12 @@ def pytest_pyfunc_call(pyfuncitem):
 | 
				
			||||||
def pytest_generate_tests(metafunc):
 | 
					def pytest_generate_tests(metafunc):
 | 
				
			||||||
    """ generate (multiple) parametrized calls to a test function."""
 | 
					    """ generate (multiple) parametrized calls to a test function."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@hookspec(firstresult=True)
 | 
				
			||||||
 | 
					def pytest_make_parametrize_id(config, val):
 | 
				
			||||||
 | 
					    """Return a user-friendly string representation of the given ``val`` that will be used
 | 
				
			||||||
 | 
					    by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -------------------------------------------------------------------------
 | 
					# -------------------------------------------------------------------------
 | 
				
			||||||
# generic runtest related hooks
 | 
					# generic runtest related hooks
 | 
				
			||||||
# -------------------------------------------------------------------------
 | 
					# -------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -342,6 +342,9 @@ def pytest_pycollect_makeitem(collector, name, obj):
 | 
				
			||||||
                res = list(collector._genfunctions(name, obj))
 | 
					                res = list(collector._genfunctions(name, obj))
 | 
				
			||||||
            outcome.force_result(res)
 | 
					            outcome.force_result(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pytest_make_parametrize_id(config, val):
 | 
				
			||||||
 | 
					    return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def is_generator(func):
 | 
					def is_generator(func):
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        return _pytest._code.getrawcode(func).co_flags & 32 # generator function
 | 
					        return _pytest._code.getrawcode(func).co_flags & 32 # generator function
 | 
				
			||||||
| 
						 | 
					@ -1038,7 +1041,7 @@ class Metafunc(FuncargnamesCompatAttr):
 | 
				
			||||||
        if ids and len(ids) != len(argvalues):
 | 
					        if ids and len(ids) != len(argvalues):
 | 
				
			||||||
            raise ValueError('%d tests specified with %d ids' %(
 | 
					            raise ValueError('%d tests specified with %d ids' %(
 | 
				
			||||||
                             len(argvalues), len(ids)))
 | 
					                             len(argvalues), len(ids)))
 | 
				
			||||||
        ids = idmaker(argnames, argvalues, idfn, ids)
 | 
					        ids = idmaker(argnames, argvalues, idfn, ids, self.config)
 | 
				
			||||||
        newcalls = []
 | 
					        newcalls = []
 | 
				
			||||||
        for callspec in self._calls or [CallSpec2(self)]:
 | 
					        for callspec in self._calls or [CallSpec2(self)]:
 | 
				
			||||||
            for param_index, valset in enumerate(argvalues):
 | 
					            for param_index, valset in enumerate(argvalues):
 | 
				
			||||||
| 
						 | 
					@ -1138,7 +1141,7 @@ else:
 | 
				
			||||||
            return val.encode('unicode-escape')
 | 
					            return val.encode('unicode-escape')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _idval(val, argname, idx, idfn):
 | 
					def _idval(val, argname, idx, idfn, config=None):
 | 
				
			||||||
    if idfn:
 | 
					    if idfn:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            s = idfn(val)
 | 
					            s = idfn(val)
 | 
				
			||||||
| 
						 | 
					@ -1147,6 +1150,11 @@ def _idval(val, argname, idx, idfn):
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config:
 | 
				
			||||||
 | 
					        hook_id = config.hook.pytest_make_parametrize_id(config=config, val=val)
 | 
				
			||||||
 | 
					        if hook_id:
 | 
				
			||||||
 | 
					            return hook_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if isinstance(val, (bytes, str)) or (_PY2 and isinstance(val, unicode)):
 | 
					    if isinstance(val, (bytes, str)) or (_PY2 and isinstance(val, unicode)):
 | 
				
			||||||
        return _escape_strings(val)
 | 
					        return _escape_strings(val)
 | 
				
			||||||
    elif isinstance(val, (float, int, bool, NoneType)):
 | 
					    elif isinstance(val, (float, int, bool, NoneType)):
 | 
				
			||||||
| 
						 | 
					@ -1159,16 +1167,16 @@ def _idval(val, argname, idx, idfn):
 | 
				
			||||||
        return val.__name__
 | 
					        return val.__name__
 | 
				
			||||||
    return str(argname)+str(idx)
 | 
					    return str(argname)+str(idx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _idvalset(idx, valset, argnames, idfn, ids):
 | 
					def _idvalset(idx, valset, argnames, idfn, ids, config=None):
 | 
				
			||||||
    if ids is None or ids[idx] is None:
 | 
					    if ids is None or ids[idx] is None:
 | 
				
			||||||
        this_id = [_idval(val, argname, idx, idfn)
 | 
					        this_id = [_idval(val, argname, idx, idfn, config)
 | 
				
			||||||
                   for val, argname in zip(valset, argnames)]
 | 
					                   for val, argname in zip(valset, argnames)]
 | 
				
			||||||
        return "-".join(this_id)
 | 
					        return "-".join(this_id)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return _escape_strings(ids[idx])
 | 
					        return _escape_strings(ids[idx])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def idmaker(argnames, argvalues, idfn=None, ids=None):
 | 
					def idmaker(argnames, argvalues, idfn=None, ids=None, config=None):
 | 
				
			||||||
    ids = [_idvalset(valindex, valset, argnames, idfn, ids)
 | 
					    ids = [_idvalset(valindex, valset, argnames, idfn, ids, config)
 | 
				
			||||||
           for valindex, valset in enumerate(argvalues)]
 | 
					           for valindex, valset in enumerate(argvalues)]
 | 
				
			||||||
    if len(set(ids)) != len(ids):
 | 
					    if len(set(ids)) != len(ids):
 | 
				
			||||||
        # The ids are not unique
 | 
					        # The ids are not unique
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -470,6 +470,7 @@ you can use the following hook:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autofunction:: pytest_pycollect_makeitem
 | 
					.. autofunction:: pytest_pycollect_makeitem
 | 
				
			||||||
.. autofunction:: pytest_generate_tests
 | 
					.. autofunction:: pytest_generate_tests
 | 
				
			||||||
 | 
					.. autofunction:: pytest_make_parametrize_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After collection is complete, you can modify the order of
 | 
					After collection is complete, you can modify the order of
 | 
				
			||||||
items, delete or otherwise amend the test items:
 | 
					items, delete or otherwise amend the test items:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1156,3 +1156,21 @@ class TestMarkersWithParametrization:
 | 
				
			||||||
        """)
 | 
					        """)
 | 
				
			||||||
        reprec = testdir.inline_run()
 | 
					        reprec = testdir.inline_run()
 | 
				
			||||||
        reprec.assertoutcome(passed=2)
 | 
					        reprec.assertoutcome(passed=2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_pytest_make_parametrize_id(self, testdir):
 | 
				
			||||||
 | 
					        testdir.makeconftest("""
 | 
				
			||||||
 | 
					            def pytest_make_parametrize_id(config, val):
 | 
				
			||||||
 | 
					                return str(val * 2)
 | 
				
			||||||
 | 
					        """)
 | 
				
			||||||
 | 
					        testdir.makepyfile("""
 | 
				
			||||||
 | 
					                import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @pytest.mark.parametrize("x", range(2))
 | 
				
			||||||
 | 
					                def test_func(x):
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					                """)
 | 
				
			||||||
 | 
					        result = testdir.runpytest("-v")
 | 
				
			||||||
 | 
					        result.stdout.fnmatch_lines([
 | 
				
			||||||
 | 
					            "*test_func*0*PASS*",
 | 
				
			||||||
 | 
					            "*test_func*2*PASS*",
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue