Pytest: 4.4.0 regression for pdbcls

Created on 3 Apr 2019  路  17Comments  路  Source: pytest-dev/pytest

In my code I'm trying to define my own debugger class, which in this case is a bit of a hack to use ipdb I have a file with:

class Debugger(object):
    quitting = None

    def set_trace(self, frame):
        import ipdb  # noqa

        return ipdb.set_trace(frame)  # noqa

    def reset(self, *args, **kwargs):
        from ipdb.__main__ import _init_pdb

        pdb_obj = _init_pdb()
        pdb_obj.botframe = None  # not sure. exception otherwise at quit
        return pdb_obj.reset(*args, **kwargs)

    def interaction(self, *args, **kwargs):
        from ipdb.__main__ import _init_pdb

        pdb_obj = _init_pdb()
        pdb_obj.botframe = None  # not sure. exception otherwise at quit
        return pdb_obj.interaction(*args, **kwargs)

The code is now unable to import any class in my codebase at that step. I just get No module name X for every module in my local repository.

debugging regression

All 17 comments

What --pdbcls setting are you using?
What is the actual error?
Is it with --pdb or pdb.set_trace?

Here is the stacktrace for 4.3.1

> /usr/local/lib/python3.6/site-packages/_pytest/debugging.py(41)pytest_configure()
-> __import__(modname)
(Pdb) l
 36
 37     def pytest_configure(config):
 38         if config.getvalue("usepdb_cls"):
 39             modname, classname = config.getvalue("usepdb_cls").split(":")
 40             import pdb; pdb.set_trace()
 41  ->         __import__(modname)
 42             pdb_cls = getattr(sys.modules[modname], classname)
 43         else:
 44             pdb_cls = pdb.Pdb
 45
 46         if config.getvalue("trace"):
(Pdb) w
  /usr/local/bin/pytest(11)<module>()
-> sys.exit(main())
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(79)main()
-> return config.hook.pytest_cmdline_main(config=config)
  /usr/local/lib/python3.6/site-packages/pluggy/hooks.py(289)__call__()
-> return self._hookexec(self, self.get_hookimpls(), kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(68)_hookexec()
-> return self._inner_hookexec(hook, methods, kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(62)<lambda>()
-> firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  /usr/local/lib/python3.6/site-packages/pluggy/callers.py(187)_multicall()
-> res = hook_impl.function(*args)
  /usr/local/lib/python3.6/site-packages/_pytest/main.py(242)pytest_cmdline_main()
-> return wrap_session(config, _main)
  /usr/local/lib/python3.6/site-packages/_pytest/main.py(205)wrap_session()
-> config._do_configure()
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(638)_do_configure()
-> self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
  /usr/local/lib/python3.6/site-packages/pluggy/hooks.py(311)call_historic()
-> res = self._hookexec(self, self.get_hookimpls(), kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(68)_hookexec()
-> return self._inner_hookexec(hook, methods, kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(62)<lambda>()
-> firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  /usr/local/lib/python3.6/site-packages/pluggy/callers.py(187)_multicall()
-> res = hook_impl.function(*args)
> /usr/local/lib/python3.6/site-packages/_pytest/debugging.py(41)pytest_configure()
-> __import__(modname)
(Pdb) import common
(Pdb) from common.debugger import Debugger
(Pdb) Debugger
<class 'common.debugger.Debugger'>

On 4.4.0

/app/nextaccounting # pytest -s journal_entry/tests/test_constants.py
> /usr/local/lib/python3.6/site-packages/_pytest/debugging.py(25)_validate_usepdb_cls()
-> __import__(modname)
(Pdb) w
  /usr/local/bin/pytest(11)<module>()
-> sys.exit(main())
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(60)main()
-> config = _prepareconfig(args, plugins)
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(201)_prepareconfig()
-> pluginmanager=pluginmanager, args=args
  /usr/local/lib/python3.6/site-packages/pluggy/hooks.py(289)__call__()
-> return self._hookexec(self, self.get_hookimpls(), kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(68)_hookexec()
-> return self._inner_hookexec(hook, methods, kwargs)
  /usr/local/lib/python3.6/site-packages/pluggy/manager.py(62)<lambda>()
-> firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  /usr/local/lib/python3.6/site-packages/pluggy/callers.py(187)_multicall()
-> res = hook_impl.function(*args)
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(679)pytest_cmdline_parse()
-> self.parse(args)
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(896)parse()
-> self._preparse(args, addopts=addopts)
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(832)_preparse()
-> self._validate_args(self.getini("addopts"), "via addopts config") + args
  /usr/local/lib/python3.6/site-packages/_pytest/config/__init__.py(814)_validate_args()
-> args, namespace=copy.copy(self.option)
  /usr/local/lib/python3.6/site-packages/_pytest/config/argparsing.py(117)parse_known_and_unknown_args()
-> return optparser.parse_known_args(args, namespace=namespace)
  /usr/local/lib/python3.6/argparse.py(1766)parse_known_args()
-> namespace, args = self._parse_known_args(args, namespace)
  /usr/local/lib/python3.6/argparse.py(1972)_parse_known_args()
-> start_index = consume_optional(start_index)
  /usr/local/lib/python3.6/argparse.py(1912)consume_optional()
-> take_action(action, args, option_string)
  /usr/local/lib/python3.6/argparse.py(1824)take_action()
-> argument_values = self._get_values(action, argument_strings)
  /usr/local/lib/python3.6/argparse.py(2265)_get_values()
-> value = self._get_value(action, arg_string)
  /usr/local/lib/python3.6/argparse.py(2294)_get_value()
-> result = type_func(arg_string)
> /usr/local/lib/python3.6/site-packages/_pytest/debugging.py(25)_validate_usepdb_cls()
-> __import__(modname)
(Pdb) import common
*** ModuleNotFoundError: No module named 'common'
(Pdb) from common.debugger import Debugger
*** ModuleNotFoundError: No module named 'common'

And my setting is --pdbcls=common.debugger:Debugger

Just tried --pdbcls ipdb:__main__.debugger_cls and that appears to work already.

The problem with 4.4.0 appears to be that it stops in _validate_usepdb_cls, i.e. there might be an issue with regard to passed frame offsets, or that tracing is enabled too early already.

The question about invoking remains: is this due to using set_trace yourself, or via an exception and using --pdb? - I wonder why you are at pytest_configure in the first example already.

I'm using set_trace myself, I just put a breakpoint in to illustrate that it breaks during setup, before it runs any tests. the import fails.

Ah, ok - likely caused by https://github.com/pytest-dev/pytest/commit/84555c89 then.

Anyway, does using ipdb:__main__.debugger_cls work for you, too?

The reason is likely that validation gets done now earlier, and your sys.path might not be modified by then already.

Yeah, that should do the trick. IF I did want to extend the debugger class in any way, not being able to load a local module isn't the best. It's a regression albeit not one that affects me.

Yeah, I suspect that's the issue.

So, do you get an UsageError after all now?

Using ipdb:__main__.debugger_cls works for me. That seems to do what I was already doing with the changes that were introduced in 4.4.0.

That being said, if I wanted to expend the debugger class I cannot import one local to my project which is a new limitation. So I don't have the option of doing that anymore, as I was before the changes in 4.4.0

Yes, I see that it is a regression.. :)

I will fix it - after all it makes sense to not unnecessarily import it in the first place, i.e. make this more lazy.

Thanks, and thanks for the suggestion about using ipdb directly instead based on the new version. In this case it just simplified what I was already doing.

Yeah, I'm glad the new feature works for you - and somehow glad that it broke your current setup, since you might not have noticed otherwise.. :)

5041 should fix it. I have some stashed change to make this more lazily even, but that is for later.

Just for reference: the fix for this (later import) caused a regression with ipdb (https://github.com/pytest-dev/pytest/issues/4008#issuecomment-495063575).

Was this page helpful?
0 / 5 - 0 ratings