Skip to content

Commit

Permalink
Merge pull request #4855 from blueyed/pdbcls-attr
Browse files Browse the repository at this point in the history
--pdbcls: improve validation, and allow for "mod:attr.class"
  • Loading branch information
nicoddemus committed Mar 1, 2019
2 parents 42561db + f7a3e00 commit 84555c8
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 5 deletions.
4 changes: 4 additions & 0 deletions changelog/4855.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The ``--pdbcls`` option handles classes via module attributes now (e.g.
``pdb:pdb.Pdb`` with `pdb++`_), and its validation was improved.

.. _pdb++: https://pypi.org/project/pdbpp/
34 changes: 29 additions & 5 deletions src/_pytest/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import division
from __future__ import print_function

import argparse
import pdb
import sys
from doctest import UnexpectedException
Expand All @@ -11,6 +12,31 @@
from _pytest.config import hookimpl


def _validate_usepdb_cls(value):
try:
modname, classname = value.split(":")
except ValueError:
raise argparse.ArgumentTypeError(
"{!r} is not in the format 'modname:classname'".format(value)
)

try:
__import__(modname)
mod = sys.modules[modname]

# Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
parts = classname.split(".")
pdb_cls = getattr(mod, parts[0])
for part in parts[1:]:
pdb_cls = getattr(pdb_cls, part)

return pdb_cls
except Exception as exc:
raise argparse.ArgumentTypeError(
"could not get pdb class for {!r}: {}".format(value, exc)
)


def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption(
Expand All @@ -23,6 +49,7 @@ def pytest_addoption(parser):
"--pdbcls",
dest="usepdb_cls",
metavar="modulename:classname",
type=_validate_usepdb_cls,
help="start a custom interactive Python debugger on errors. "
"For example: --pdbcls=IPython.terminal.debugger:TerminalPdb",
)
Expand All @@ -35,11 +62,8 @@ def pytest_addoption(parser):


def pytest_configure(config):
if config.getvalue("usepdb_cls"):
modname, classname = config.getvalue("usepdb_cls").split(":")
__import__(modname)
pdb_cls = getattr(sys.modules[modname], classname)
else:
pdb_cls = config.getvalue("usepdb_cls")
if not pdb_cls:
pdb_cls = pdb.Pdb

if config.getvalue("trace"):
Expand Down
19 changes: 19 additions & 0 deletions testing/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from __future__ import division
from __future__ import print_function

import argparse
import os
import platform
import sys

import _pytest._code
import pytest
from _pytest.debugging import _validate_usepdb_cls

try:
breakpoint
Expand Down Expand Up @@ -688,6 +690,23 @@ def test_pdb_custom_cls(self, testdir, custom_pdb_calls):
result.stdout.fnmatch_lines(["*NameError*xxx*", "*1 error*"])
assert custom_pdb_calls == ["init", "reset", "interaction"]

def test_pdb_custom_cls_invalid(self, testdir):
result = testdir.runpytest_inprocess("--pdbcls=invalid")
result.stderr.fnmatch_lines(
[
"*: error: argument --pdbcls: 'invalid' is not in the format 'modname:classname'"
]
)

def test_pdb_validate_usepdb_cls(self, testdir):
assert _validate_usepdb_cls("os.path:dirname.__name__") == "dirname"

with pytest.raises(
argparse.ArgumentTypeError,
match=r"^could not get pdb class for 'pdb:DoesNotExist': .*'DoesNotExist'",
):
_validate_usepdb_cls("pdb:DoesNotExist")

def test_pdb_custom_cls_without_pdb(self, testdir, custom_pdb_calls):
p1 = testdir.makepyfile("""xxx """)
result = testdir.runpytest_inprocess("--pdbcls=_pytest:_CustomPdb", p1)
Expand Down

0 comments on commit 84555c8

Please sign in to comment.