[prerelease] AttributeError: module 'homeassistant.setup' has no attribute '__code__'
See original GitHub issueVia Twitter, @cgtobi reports:
Not sure if this is a regression, but the same test works with 6.2.5.
And indeed I can reproduce the failure via tox -e py39 -- -x tests/test_setup.py
in the home-assistant/core repository (after sneaking in the pytest main branch via pip once the initial run passed - note that it will need tox-pip-version
and setting things up will take quite a while):
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ERROR at setup of TestSetup.test_validate_component_config βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[gw0] linux -- Python 3.9.9 /home/florian/tmp/core/.tox/py39/bin/python
cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x7f01d2b9d4c0>, when = 'setup', reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: "Callable[[], TResult]",
when: "Literal['collect', 'setup', 'call', 'teardown']",
reraise: Optional[
Union[Type[BaseException], Tuple[Type[BaseException], ...]]
] = None,
) -> "CallInfo[TResult]":
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
start = timing.time()
precise_start = timing.perf_counter()
try:
> result: Optional[TResult] = func()
../../proj/pytest/src/_pytest/runner.py:340:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../proj/pytest/src/_pytest/runner.py:261: in <lambda>
lambda: ihook(item=item, **kwds), when=when, reraise=reraise
.tox/py39/lib/python3.9/site-packages/pluggy/_hooks.py:265: in __call__
return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
.tox/py39/lib/python3.9/site-packages/pluggy/_manager.py:80: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
../../proj/pytest/src/_pytest/runner.py:156: in pytest_runtest_setup
item.session._setupstate.setup(item)
../../proj/pytest/src/_pytest/runner.py:501: in setup
raise exc
../../proj/pytest/src/_pytest/runner.py:498: in setup
col.setup()
../../proj/pytest/src/_pytest/python.py:1694: in setup
self._request._fillfixtures()
../../proj/pytest/src/_pytest/fixtures.py:578: in _fillfixtures
item.funcargs[argname] = self.getfixturevalue(argname)
../../proj/pytest/src/_pytest/fixtures.py:591: in getfixturevalue
fixturedef = self._get_active_fixturedef(argname)
../../proj/pytest/src/_pytest/fixtures.py:610: in _get_active_fixturedef
self._compute_fixture_value(fixturedef)
../../proj/pytest/src/_pytest/fixtures.py:693: in _compute_fixture_value
fixturedef.execute(request=subrequest)
../../proj/pytest/src/_pytest/fixtures.py:1068: in execute
result = hook.pytest_fixture_setup(fixturedef=self, request=request)
.tox/py39/lib/python3.9/site-packages/pluggy/_hooks.py:265: in __call__
return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
.tox/py39/lib/python3.9/site-packages/pluggy/_manager.py:80: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
../../proj/pytest/src/_pytest/fixtures.py:1122: in pytest_fixture_setup
result = call_fixture_func(fixturefunc, request, kwargs)
../../proj/pytest/src/_pytest/fixtures.py:917: in call_fixture_func
fixture_result = next(generator)
../../proj/pytest/src/_pytest/python.py:540: in xunit_setup_module_fixture
_call_with_optional_argument(setup_module, request.module)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
func = <module 'homeassistant.setup' from '/home/florian/tmp/core/homeassistant/setup.py'>, arg = <module 'tests.test_setup' from '/home/florian/tmp/core/tests/test_setup.py'>
def _call_with_optional_argument(func, arg) -> None:
"""Call the given function with the given argument if func accepts one argument, otherwise
calls func without arguments."""
> arg_count = func.__code__.co_argcount
E AttributeError: module 'homeassistant.setup' has no attribute '__code__'
../../proj/pytest/src/_pytest/python.py:749: AttributeError
Relevant code here: https://github.com/home-assistant/core/blob/dev/tests/test_setup.py
I was able to bisect this to 89f0b5b5a2f485f77999d15cb9437ad9234003a1, which is:
I suspect the from homeassistant import config_entries, setup
results in a setup
name inside the test module, which pytest now picks up as a nose-style setup function but did not before or something?
cc @bluetech
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (7 by maintainers)
Btw thanks for the report @cgtobi and for the investigation @The-Compiler!
I just took a quick look at this with @cgtobi - Home Assistant actually uses nose-style
setup
/teardown
in a couple of places, so-p no:nose
leads to some issues.I was wondering why this worked just fine for them despite the
setup
import in earlier pytest versions.I think the reason is that before, we used
call_optional
for module-level setup, which silently ignores non-callable objects.However, with #9273, we now call it unconditionally despite it being a module object, which then results in the
AttributeError
for__code__
.Despite surely being a bit unorthodox in moduleassistantβs code, I think this is a regression. Not sure what the best behavior here would be:
setup
/teardown
into an error (though we might want to deprecate it first, dunno?)