From 79927428d1c962eacc2adbe8097ded23fea2ddf4 Mon Sep 17 00:00:00 2001 From: palaviv Date: Mon, 25 Apr 2016 17:11:47 +0300 Subject: [PATCH 1/4] Added pytest_make_parametrize_id hook --- _pytest/hookspec.py | 6 ++++++ _pytest/python.py | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index 60e9b47d2..5d70ea1c3 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -133,6 +133,12 @@ def pytest_deselected(items): def pytest_make_collect_report(collector): """ perform ``collector.collect()`` and return a CollectReport. """ +@hookspec(firstresult=True) +def pytest_make_parametrize_id(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``. + """ + # ------------------------------------------------------------------------- # Python test function related hooks # ------------------------------------------------------------------------- diff --git a/_pytest/python.py b/_pytest/python.py index 6785892d9..20c6a4de6 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -342,6 +342,9 @@ def pytest_pycollect_makeitem(collector, name, obj): res = list(collector._genfunctions(name, obj)) outcome.force_result(res) +def pytest_make_parametrize_id(val): + return None + def is_generator(func): try: return _pytest._code.getrawcode(func).co_flags & 32 # generator function @@ -1030,7 +1033,7 @@ class Metafunc(FuncargnamesCompatAttr): if ids and len(ids) != len(argvalues): raise ValueError('%d tests specified with %d ids' %( len(argvalues), len(ids))) - ids = idmaker(argnames, argvalues, idfn, ids) + ids = idmaker(argnames, argvalues, idfn, ids, self.config) newcalls = [] for callspec in self._calls or [CallSpec2(self)]: for param_index, valset in enumerate(argvalues): @@ -1130,7 +1133,7 @@ else: return val.encode('unicode-escape') -def _idval(val, argname, idx, idfn): +def _idval(val, argname, idx, idfn, config): if idfn: try: s = idfn(val) @@ -1139,6 +1142,11 @@ def _idval(val, argname, idx, idfn): except Exception: pass + if config: + hook_id = config.hook.pytest_make_parametrize_id(val=val) + if hook_id: + return hook_id + if isinstance(val, (bytes, str)) or (_PY2 and isinstance(val, unicode)): return _escape_strings(val) elif isinstance(val, (float, int, bool, NoneType)): @@ -1151,16 +1159,16 @@ def _idval(val, argname, idx, idfn): return val.__name__ return str(argname)+str(idx) -def _idvalset(idx, valset, argnames, idfn, ids): +def _idvalset(idx, valset, argnames, idfn, ids, config): 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)] return "-".join(this_id) else: return _escape_strings(ids[idx]) -def idmaker(argnames, argvalues, idfn=None, ids=None): - ids = [_idvalset(valindex, valset, argnames, idfn, ids) +def idmaker(argnames, argvalues, idfn=None, ids=None, config=None): + ids = [_idvalset(valindex, valset, argnames, idfn, ids, config) for valindex, valset in enumerate(argvalues)] if len(set(ids)) != len(ids): # The ids are not unique From b9faf78d519c116273a0c951b82f5405e9369ff7 Mon Sep 17 00:00:00 2001 From: palaviv Date: Mon, 25 Apr 2016 17:48:28 +0300 Subject: [PATCH 2/4] Added test_pytest_make_parametrize_id --- _pytest/python.py | 4 ++-- testing/python/metafunc.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/_pytest/python.py b/_pytest/python.py index 20c6a4de6..aa80c8640 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1133,7 +1133,7 @@ else: return val.encode('unicode-escape') -def _idval(val, argname, idx, idfn, config): +def _idval(val, argname, idx, idfn, config=None): if idfn: try: s = idfn(val) @@ -1159,7 +1159,7 @@ def _idval(val, argname, idx, idfn, config): return val.__name__ return str(argname)+str(idx) -def _idvalset(idx, valset, argnames, idfn, ids, config): +def _idvalset(idx, valset, argnames, idfn, ids, config=None): if ids is None or ids[idx] is None: this_id = [_idval(val, argname, idx, idfn, config) for val, argname in zip(valset, argnames)] diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 6ae3ca43f..28b3e0d64 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1156,3 +1156,21 @@ class TestMarkersWithParametrization: """) reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + + def test_pytest_make_parametrize_id(self, testdir): + testdir.makeconftest(""" + def pytest_make_parametrize_id(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*", + ]) From 53429ed8b867ea129d3268ad034b191e3613a90b Mon Sep 17 00:00:00 2001 From: palaviv Date: Mon, 25 Apr 2016 18:03:34 +0300 Subject: [PATCH 3/4] Added hook to plugin docs and new CHANGELOG record --- CHANGELOG.rst | 3 ++- _pytest/hookspec.py | 12 ++++++------ doc/en/writing_plugins.rst | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 18e0e3c64..b4b7bffa6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,7 +27,8 @@ whether to filter the traceback based on the ``ExceptionInfo`` object passed to it. -* +* New ``pytest_make_parametrize_id`` hook. + Thanks `@palaviv`_ for the PR. **Changes** diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index 5d70ea1c3..b5125ffdb 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -133,12 +133,6 @@ def pytest_deselected(items): def pytest_make_collect_report(collector): """ perform ``collector.collect()`` and return a CollectReport. """ -@hookspec(firstresult=True) -def pytest_make_parametrize_id(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``. - """ - # ------------------------------------------------------------------------- # Python test function related hooks # ------------------------------------------------------------------------- @@ -162,6 +156,12 @@ def pytest_pyfunc_call(pyfuncitem): def pytest_generate_tests(metafunc): """ generate (multiple) parametrized calls to a test function.""" +@hookspec(firstresult=True) +def pytest_make_parametrize_id(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 # ------------------------------------------------------------------------- diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index cc346aaa8..38d47bf6d 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -470,6 +470,7 @@ you can use the following hook: .. autofunction:: pytest_pycollect_makeitem .. autofunction:: pytest_generate_tests +.. autofunction:: pytest_make_parametrize_id After collection is complete, you can modify the order of items, delete or otherwise amend the test items: From 9733127951b1737e54eb9902a0835891f1dbee56 Mon Sep 17 00:00:00 2001 From: palaviv Date: Tue, 26 Apr 2016 10:23:57 +0300 Subject: [PATCH 4/4] pytest_make_parametrize_id receive config object --- _pytest/hookspec.py | 2 +- _pytest/python.py | 4 ++-- testing/python/metafunc.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index b5125ffdb..424ee2069 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -157,7 +157,7 @@ def pytest_generate_tests(metafunc): """ generate (multiple) parametrized calls to a test function.""" @hookspec(firstresult=True) -def pytest_make_parametrize_id(val): +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``. """ diff --git a/_pytest/python.py b/_pytest/python.py index aa80c8640..9a97e7236 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -342,7 +342,7 @@ def pytest_pycollect_makeitem(collector, name, obj): res = list(collector._genfunctions(name, obj)) outcome.force_result(res) -def pytest_make_parametrize_id(val): +def pytest_make_parametrize_id(config, val): return None def is_generator(func): @@ -1143,7 +1143,7 @@ def _idval(val, argname, idx, idfn, config=None): pass if config: - hook_id = config.hook.pytest_make_parametrize_id(val=val) + hook_id = config.hook.pytest_make_parametrize_id(config=config, val=val) if hook_id: return hook_id diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 28b3e0d64..6ce6cb751 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -1159,7 +1159,7 @@ class TestMarkersWithParametrization: def test_pytest_make_parametrize_id(self, testdir): testdir.makeconftest(""" - def pytest_make_parametrize_id(val): + def pytest_make_parametrize_id(config, val): return str(val * 2) """) testdir.makepyfile("""