pytest-dev / pytest-bdd

BDD library for the py.test runner
https://pytest-bdd.readthedocs.io/en/latest/
MIT License
1.3k stars 219 forks source link

RecursionError when calling `pytest --help` #346

Open Phlogistique opened 4 years ago

Phlogistique commented 4 years ago

When calling pytest --help I get the following backtrace:

noes-MBP:... noe$ pytest --help
Traceback (most recent call last):
  File "/Users/noe/.pyenv/versions/.../bin/pytest", line 10, in <module>
    sys.exit(main())
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/config/__init__.py", line 59, in main
    config = _prepareconfig(args, plugins)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/config/__init__.py", line 209, in _prepareconfig
    pluginmanager=pluginmanager, args=args
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/manager.py", line 92, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/manager.py", line 86, in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/helpconfig.py", line 89, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/config/__init__.py", line 720, in pytest_cmdline_parse
    self.parse(args)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/config/__init__.py", line 928, in parse
    self._preparse(args, addopts=addopts)
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/config/__init__.py", line 874, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/pluggy/manager.py", line 297, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/importlib_metadata/__init__.py", line 94, in load
    module = import_module(match.group('module'))
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "/Users/noe/.pyenv/versions/3.7.3/envs/.../lib/python3.7/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/plugin.py", line 21, in <module>
    @then('trace')
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/steps.py", line 186, in decorator
    setattr(get_caller_module(), get_step_fixture_name(parsed_step_name, step_type), lazy_step_func)
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/steps.py", line 197, in get_caller_module
    return get_caller_module(depth=depth)
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/steps.py", line 197, in get_caller_module
    return get_caller_module(depth=depth)
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/steps.py", line 197, in get_caller_module
    return get_caller_module(depth=depth)
  [Previous line repeated 968 more times]
  File "/Users/noe/.pyenv/versions/.../lib/python3.7/site-packages/pytest_bdd/steps.py", line 195, in get_caller_module
    module = inspect.getmodule(frame)
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/inspect.py", line 725, in getmodule
    file = getabsfile(object, _filename)
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/inspect.py", line 708, in getabsfile
    _filename = getsourcefile(object) or getfile(object)
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/inspect.py", line 684, in getsourcefile
    filename = getfile(object)
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/inspect.py", line 644, in getfile
    if ismodule(object):
  File "/Users/noe/.pyenv/versions/3.7.3/lib/python3.7/inspect.py", line 70, in ismodule
    return isinstance(object, types.ModuleType)
RecursionError: maximum recursion depth exceeded while calling a Python object

Workarounds

How to reproduce

Sorry, I do not have a proper reproduction scenario.

Here is the output of pip freeze | grep pytest:

pytest==5.1.2
pytest-bdd==3.2.1
pytest-cov==2.7.1
pytest-dotenv==0.4.0
pytest-forked==1.1.3
pytest-xdist==1.29.0

Investigation

The function get_caller_module does look fishy to me:

def get_caller_module(depth=2):
    """Return the module of the caller."""
    frame = sys._getframe(depth)
    module = inspect.getmodule(frame)
    if module is None:
        return get_caller_module(depth=depth)
    return module

To me it looks like it's trying to loop towards the outermost stackframe, but forgets to change depth and therefore loops recursively towards the innermost stackframe, until triggering a recursion error.

youtux commented 4 years ago

Hi @Phlogistique, I'm not sure how to help you here, since there are no steps to reproduce the issue.

The-Compiler commented 4 years ago

While I'm not able to reproduce this, I agree get_caller_module looks weird. If module was None, why would calling the function again with the exact same arguments change anything?

Phlogistique commented 4 years ago

A colleague has experienced the same problem on his machine. Like for me, the problem disappeared when re-installing pytest_bdd.

Would you accept a patch to get_caller_module that tries to fix this?

youtux commented 4 years ago

@Phlogistique yes