pytest.mark.parametrize cannot locate argument name when there is another decorator
See original GitHub issueThis is a feature request.
Description
When pytest.mark.parametrize
is combined with another decorator that takes (*args, **kwargs)
, it can’t find the correct argument name, see examples below.
Not working
import pytest
def mydeco(foo):
def wrapped(*args, **kwargs):
print('wrapped!')
return foo(*args, **kwargs)
return wrapped
@pytest.mark.parametrize('x', [1, 2, 3])
@mydeco
def test_func(x):
assert x == 233
(opengl) [bate@archit taichi]$ pytest asas.py
============================================= test session starts =============================================
platform linux -- Python 3.8.1, pytest-5.3.4, py-1.8.1, pluggy-0.13.1
rootdir: /home/bate/Develop/taichi
plugins: xdist-1.31.0, forked-1.1.3
collected 0 items / 1 error
=================================================== ERRORS ====================================================
__________________________________________ ERROR collecting asas.py ___________________________________________
In wrapped: function uses no argument 'x'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================== 1 error in 0.06s ===============================================
Working
import pytest
@pytest.mark.parametrize('x', [1, 2, 3])
def test_func(x):
assert x == 233
(opengl) [bate@archit taichi]$ pytest asas.py
============================================= test session starts =============================================
platform linux -- Python 3.8.1, pytest-5.3.4, py-1.8.1, pluggy-0.13.1
rootdir: /home/bate/Develop/taichi
plugins: xdist-1.31.0, forked-1.1.3
collected 3 items
asas.py FFF [100%]
================================================== FAILURES ===================================================
________________________________________________ test_func[1] _________________________________________________
x = 1
@pytest.mark.parametrize('x', [1, 2, 3])
def test_func(x):
> assert x == 233
E assert 1 == 233
asas.py:5: AssertionError
________________________________________________ test_func[2] _________________________________________________
x = 2
@pytest.mark.parametrize('x', [1, 2, 3])
def test_func(x):
> assert x == 233
E assert 2 == 233
asas.py:5: AssertionError
________________________________________________ test_func[3] _________________________________________________
x = 3
@pytest.mark.parametrize('x', [1, 2, 3])
def test_func(x):
> assert x == 233
E assert 3 == 233
asas.py:5: AssertionError
============================================== 3 failed in 0.03s ==============================================
Why we need this?
@k-ye was trying to apply a test for all architectures using our @ti.all_archs
decorator. It must be the last decorator to function so that ti.init()
could be called for each test.
Then, we want to use parametrize
and failed due to the reason shown above.
Discussion: https://github.com/taichi-dev/taichi/pull/527#issuecomment-590261599
Possible solutions:
Thanks to @k-ye:
def parametrize(argnames: str, argvalues):
# @pytest.mark.parametrize only works for canonical function args, and doesn't
# support *args or **kwargs. This makes it difficult to play along with other
# decorators like @ti.all_archs. As a result, we implement our own.
argnames = [s.strip() for s in argnames.split(',')]
def iterable(x):
try:
_ = iter(x)
return True
except:
return False
def decorator(test):
def wrapped(*test_args, **test_kwargs):
for vals in argvalues:
if isinstance(vals, str) or not iterable(vals):
vals = (vals, )
kwargs = {k: v for k, v in zip(argnames, vals)}
assert len(kwargs.keys() & test_kwargs.keys()) == 0
kwargs.update(test_kwargs)
test(*test_args, **kwargs)
return wrapped
return decorator
Related commits: https://github.com/taichi-dev/taichi/pull/527/files/11cb31b4fa1a2836ae015d5a463e4a0245b65346..2d6591825c6253bd2a01df8b3d4e2e71ed64c7a
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (3 by maintainers)
Top Results From Across the Web
Parametrizing fixtures and test functions — pytest documentation
The builtin pytest.mark.parametrize decorator enables parametrization of arguments for a test function. Here is a typical example of a test function that ...
Read more >How can I pass fixtures to pytest.mark.parameterize?
To pass fixtures as parametrized arguments, reference them as strings and use the keyword arguments indirect=['arg_name', ...] .
Read more >Deep dive into Pytest parametrization | by George Shuklin
A fixture is a function, which is automatically called by Pytest when the name of the argument (argument of the test function or...
Read more >Effective Python Testing With Pytest
parametrize creates multiple variants of a test with different values as arguments. You'll learn more about this mark shortly. You can see a ......
Read more >Python parametrized testing - The Blue Book
Fixtures may have parameters. Those parameters are passed as a list to the argument params of @pytest.fixture() decorator. Those parameters must be iterables, ......
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
It works correctly if your decorator is well-behaved and uses functools.wraps:
I don’t think this is something pytest should work around.
OK, thank you all for the input!