pytest-dev / pytest

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing
https://pytest.org
MIT License
12.19k stars 2.7k forks source link

internal error when parametrising using yield functools.partial #740

Closed pytestbot closed 9 years ago

pytestbot commented 9 years ago

Originally reported by: Dima Tisnek (BitBucket: dimaqq, GitHub: dimaqq)


Traceback is far below.

Unfortunately I cannot yet create a minimum reproducible example, because everything works, if I remove propitiatory code.

Note that all tests pass (I can remove all asserts even), but there's still an internal error.

Below is the outline of the test:

#!python
import functools
backup = []                                                                                                              

def setup():                                                                                                             
    backup[:] = secret, secret, secret
    secret.init()

def teardown():                                                                                                          
    secret, secret, secret = backup[:]                                                                                                  

def test_one(foo=123):                                                                                                   
    tmp = secret.secretObject()
    tmp.init(...)                                                                                         

def test_all():                                                                                                          
    for somearg in range(10):                                                                                            
        yield functools.partial(test_one, somearg)
#!python

(plc)[dima@bmg 18src]$ py.test -v  test_foo.py 
================================================================================== test session starts ==================================================================================
platform linux2 -- Python 2.7.9 -- py-1.4.27 -- pytest-2.7.0 -- /dima/plc/bin/python2
rootdir: /dima/tmp/sync_18/module/sync/python/src, inifile: 
plugins: incremental, ipdb, cov, random, xdist
collected 6 items 

test_foo.py::test_convert_and_filter_external_simple PASSED
[location] 
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/main.py", line 84, in wrap_session
INTERNALERROR>     doit(config, session)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/main.py", line 122, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 521, in __call__
INTERNALERROR>     return self._docall(self.methods, kwargs)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 528, in _docall
INTERNALERROR>     firstresult=self.firstresult).execute()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 394, in execute
INTERNALERROR>     res = method(*args)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/main.py", line 142, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 521, in __call__
INTERNALERROR>     return self._docall(self.methods, kwargs)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 528, in _docall
INTERNALERROR>     firstresult=self.firstresult).execute()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 393, in execute
INTERNALERROR>     return wrapped_call(method(*args), self.execute)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 113, in wrapped_call
INTERNALERROR>     return call_outcome.get_result()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 138, in get_result
INTERNALERROR>     py.builtin._reraise(*ex)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 123, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 394, in execute
INTERNALERROR>     res = method(*args)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/runner.py", line 65, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/runner.py", line 75, in runtestprotocol
INTERNALERROR>     reports.append(call_and_report(item, "call", log))
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/runner.py", line 121, in call_and_report
INTERNALERROR>     report = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 521, in __call__
INTERNALERROR>     return self._docall(self.methods, kwargs)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 528, in _docall
INTERNALERROR>     firstresult=self.firstresult).execute()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 393, in execute
INTERNALERROR>     return wrapped_call(method(*args), self.execute)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 109, in wrapped_call
INTERNALERROR>     wrap_controller.send(call_outcome)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/skipping.py", line 157, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 138, in get_result
INTERNALERROR>     py.builtin._reraise(*ex)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 123, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/core.py", line 394, in execute
INTERNALERROR>     res = method(*args)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/runner.py", line 224, in pytest_runtest_makereport
INTERNALERROR>     longrepr = item.repr_failure(excinfo)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/python.py", line 625, in repr_failure
INTERNALERROR>     return self._repr_failure_py(excinfo, style=style)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/python.py", line 618, in _repr_failure_py
INTERNALERROR>     style=style)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/main.py", line 397, in _repr_failure_py
INTERNALERROR>     self._prunetraceback(excinfo)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/_pytest/python.py", line 597, in _prunetraceback
INTERNALERROR>     code = py.code.Code(self.obj)
INTERNALERROR>   File "/dima/plc/lib/python2.7/site-packages/py/_code/code.py", line 24, in __init__
INTERNALERROR>     raise TypeError("not a code object: %r" %(rawcode,))
INTERNALERROR> TypeError: not a code object: <functools.partial object at 0x7f7a3590d788>

=============================================================================== 1 passed in 0.20 seconds ================================================================================

pytestbot commented 9 years ago

Original comment by Roman Levin (BitBucket: romanlevin, GitHub: romanlevin):


Seems to be caused by test failure passing the partialto py.code.Code, which expects its input to be a code object or have a code object in its __code__ attribute.

partials don't have a code object, hence the error.

jan-matejka commented 9 years ago

test_foo = functools.partial(do, stuff)

nicoddemus commented 9 years ago

Thanks @yaccz, here's a full example failure to the underlying problem:

import functools
def do_it(x):
    assert x == 2
test_foo = functools.partial(do_it, x=1)  
=================================== ERRORS ====================================
________________________ ERROR collecting test_foo.py _________________________
...\lib\inspect.py:752: in getargs
    raise TypeError('{!r} is not a code object'.format(co))
E   TypeError: <functools.partial object at 0x0000000003D37278> is not a code object
=============================== warning summary ===============================
WC2 X:\test_foo.py cannot collect 'test_foo' because it is not a function.
===================== 1 warnings, 1 error in 0.02 seconds =====================
nicoddemus commented 9 years ago

@dimaqq, as an workaround, you can use @pytest.mark.parametrized to write equivalent code:

@pytest.mark.parametrized('foo', range(10))
def test_one(foo=123):                                                                                                   
    tmp = secret.secretObject()
    tmp.init(...)                                                                                         

In which case you don't even need a test_all anymore.

I think @hpk42 has mentioned to discourage "yield based tests" in the docs, and IMHO the parametrized version is clearer anyway. :smile:

nicoddemus commented 9 years ago

Opened a new issue with a more specific report of the underlying problem.

Thanks @dimaqq for reporting the issue. :smile:

nicoddemus commented 9 years ago

I created a PR which fixes this, feedback is welcome.