Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

""" interactive debugging with PDB, the Python Debugger. """ 

from __future__ import absolute_import 

import pdb 

import sys 

 

import pytest 

 

 

def pytest_addoption(parser): 

    group = parser.getgroup("general") 

    group._addoption('--pdb', 

               action="store_true", dest="usepdb", default=False, 

               help="start the interactive Python debugger on errors.") 

 

def pytest_namespace(): 

    return {'set_trace': pytestPDB().set_trace} 

 

def pytest_configure(config): 

20    if config.getvalue("usepdb"): 

        config.pluginmanager.register(PdbInvoke(), 'pdbinvoke') 

 

    old = (pdb.set_trace, pytestPDB._pluginmanager) 

    def fin(): 

        pdb.set_trace, pytestPDB._pluginmanager = old 

        pytestPDB._config = None 

    pdb.set_trace = pytest.set_trace 

    pytestPDB._pluginmanager = config.pluginmanager 

    pytestPDB._config = config 

    config._cleanup.append(fin) 

 

class pytestPDB: 

    """ Pseudo PDB that defers to the real pdb. """ 

    _pluginmanager = None 

    _config = None 

 

    def set_trace(self): 

        """ invoke PDB set_trace debugging, dropping any IO capturing. """ 

        import _pytest.config 

        frame = sys._getframe().f_back 

        if self._pluginmanager is not None: 

            capman = self._pluginmanager.getplugin("capturemanager") 

            if capman: 

                capman.suspendcapture(in_=True) 

            tw = _pytest.config.create_terminal_writer(self._config) 

            tw.line() 

            tw.sep(">", "PDB set_trace (IO-capturing turned off)") 

            self._pluginmanager.hook.pytest_enter_pdb(config=self._config) 

        pdb.Pdb().set_trace(frame) 

 

 

class PdbInvoke: 

    def pytest_exception_interact(self, node, call, report): 

        capman = node.config.pluginmanager.getplugin("capturemanager") 

        if capman: 

            out, err = capman.suspendcapture(in_=True) 

            sys.stdout.write(out) 

            sys.stdout.write(err) 

        _enter_pdb(node, call.excinfo, report) 

 

    def pytest_internalerror(self, excrepr, excinfo): 

        for line in str(excrepr).split("\n"): 

            sys.stderr.write("INTERNALERROR> %s\n" %line) 

            sys.stderr.flush() 

        tb = _postmortem_traceback(excinfo) 

        post_mortem(tb) 

 

 

def _enter_pdb(node, excinfo, rep): 

    # XXX we re-use the TerminalReporter's terminalwriter 

    # because this seems to avoid some encoding related troubles 

    # for not completely clear reasons. 

    tw = node.config.pluginmanager.getplugin("terminalreporter")._tw 

    tw.line() 

    tw.sep(">", "traceback") 

    rep.toterminal(tw) 

    tw.sep(">", "entering PDB") 

    tb = _postmortem_traceback(excinfo) 

    post_mortem(tb) 

    rep._pdbshown = True 

    return rep 

 

 

def _postmortem_traceback(excinfo): 

    # A doctest.UnexpectedException is not useful for post_mortem. 

    # Use the underlying exception instead: 

    from doctest import UnexpectedException 

    if isinstance(excinfo.value, UnexpectedException): 

        return excinfo.value.exc_info[2] 

    else: 

        return excinfo._excinfo[2] 

 

 

def _find_last_non_hidden_frame(stack): 

    i = max(0, len(stack) - 1) 

    while i and stack[i][0].f_locals.get("__tracebackhide__", False): 

        i -= 1 

    return i 

 

 

def post_mortem(t): 

    class Pdb(pdb.Pdb): 

        def get_stack(self, f, t): 

            stack, i = pdb.Pdb.get_stack(self, f, t) 

            if f is None: 

                i = _find_last_non_hidden_frame(stack) 

            return stack, i 

    p = Pdb() 

    p.reset() 

    p.interaction(None, t)