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

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

""" 

support for presenting detailed information in failing assertions. 

""" 

import py 

import os 

import sys 

from _pytest.monkeypatch import monkeypatch 

from _pytest.assertion import util 

 

 

def pytest_addoption(parser): 

    group = parser.getgroup("debugconfig") 

    group.addoption('--assert', 

                    action="store", 

                    dest="assertmode", 

                    choices=("rewrite", "reinterp", "plain",), 

                    default="rewrite", 

                    metavar="MODE", 

                    help="""control assertion debugging tools.  'plain' 

                            performs no assertion debugging.  'reinterp' 

                            reinterprets assert statements after they failed 

                            to provide assertion expression information. 

                            'rewrite' (the default) rewrites assert 

                            statements in test modules on import to 

                            provide assert expression information. """) 

    group.addoption('--no-assert', 

                    action="store_true", 

                    default=False, 

                    dest="noassert", 

                    help="DEPRECATED equivalent to --assert=plain") 

    group.addoption('--nomagic', '--no-magic', 

                    action="store_true", 

                    default=False, 

                    help="DEPRECATED equivalent to --assert=plain") 

 

 

class AssertionState: 

    """State for the assertion plugin.""" 

 

    def __init__(self, config, mode): 

        self.mode = mode 

        self.trace = config.trace.root.get("assertion") 

 

 

def pytest_configure(config): 

    mode = config.getvalue("assertmode") 

48    if config.getvalue("noassert") or config.getvalue("nomagic"): 

        mode = "plain" 

60    if mode == "rewrite": 

        try: 

            import ast  # noqa 

        except ImportError: 

            mode = "reinterp" 

        else: 

            # Both Jython and CPython 2.6.0 have AST bugs that make the 

            # assertion rewriting hook malfunction. 

59   59            if (sys.platform.startswith('java') or 

                    sys.version_info[:3] == (2, 6, 0)): 

                mode = "reinterp" 

66    if mode != "plain": 

        _load_modules(mode) 

        m = monkeypatch() 

        config._cleanup.append(m.undo) 

        m.setattr(py.builtin.builtins, 'AssertionError', 

                  reinterpret.AssertionError)  # noqa 

    hook = None 

70    if mode == "rewrite": 

        hook = rewrite.AssertionRewritingHook()  # noqa 

        sys.meta_path.insert(0, hook) 

    warn_about_missing_assertion(mode) 

    config._assertstate = AssertionState(config, mode) 

    config._assertstate.hook = hook 

    config._assertstate.trace("configured with mode set to %r" % (mode,)) 

    def undo(): 

        hook = config._assertstate.hook 

exit        if hook is not None and hook in sys.meta_path: 

            sys.meta_path.remove(hook) 

    config.add_cleanup(undo) 

 

 

def pytest_collection(session): 

    # this hook is only called when test modules are collected 

    # so for example not in the master process of pytest-xdist 

    # (which does not collect test modules) 

    hook = session.config._assertstate.hook 

exit    if hook is not None: 

        hook.set_session(session) 

 

 

def _running_on_ci(): 

    """Check if we're currently running on a CI system.""" 

    env_vars = ['CI', 'BUILD_NUMBER'] 

    return any(var in os.environ for var in env_vars) 

 

 

def pytest_runtest_setup(item): 

    """Setup the pytest_assertrepr_compare hook 

 

    The newinterpret and rewrite modules will use util._reprcompare if 

    it exists to use custom reporting via the 

    pytest_assertrepr_compare hook.  This sets up this custom 

    comparison for the test. 

    """ 

    def callbinrepr(op, left, right): 

        """Call the pytest_assertrepr_compare hook and prepare the result 

 

        This uses the first result from the hook and then ensures the 

        following: 

        * Overly verbose explanations are dropped unless -vv was used or 

          running on a CI. 

        * Embedded newlines are escaped to help util.format_explanation() 

          later. 

        * If the rewrite mode is used embedded %-characters are replaced 

          to protect later % formatting. 

 

        The result can be formatted by util.format_explanation() for 

        pretty printing. 

        """ 

        hook_result = item.ihook.pytest_assertrepr_compare( 

            config=item.config, op=op, left=left, right=right) 

        for new_expl in hook_result: 

            if new_expl: 

                if (sum(len(p) for p in new_expl[1:]) > 80*8 and 

                        item.config.option.verbose < 2 and 

                        not _running_on_ci()): 

                    show_max = 10 

                    truncated_lines = len(new_expl) - show_max 

                    new_expl[show_max:] = [py.builtin._totext( 

                        'Detailed information truncated (%d more lines)' 

                        ', use "-vv" to show' % truncated_lines)] 

                new_expl = [line.replace("\n", "\\n") for line in new_expl] 

                res = py.builtin._totext("\n~").join(new_expl) 

                if item.config.getvalue("assertmode") == "rewrite": 

                    res = res.replace("%", "%%") 

                return res 

    util._reprcompare = callbinrepr 

 

 

def pytest_runtest_teardown(item): 

    util._reprcompare = None 

 

 

def pytest_sessionfinish(session): 

    hook = session.config._assertstate.hook 

exit    if hook is not None: 

        hook.session = None 

 

 

def _load_modules(mode): 

    """Lazily import assertion related code.""" 

    global rewrite, reinterpret 

    from _pytest.assertion import reinterpret  # noqa 

exit    if mode == "rewrite": 

        from _pytest.assertion import rewrite  # noqa 

 

 

def warn_about_missing_assertion(mode): 

    try: 

        assert False 

    except AssertionError: 

        pass 

    else: 

        if mode == "rewrite": 

            specifically = ("assertions which are not in test modules " 

                            "will be ignored") 

        else: 

            specifically = "failing tests may report as passing" 

 

        sys.stderr.write("WARNING: " + specifically + 

                         " because assert statements are not executed " 

                         "by the underlying Python interpreter " 

                         "(are you using python -O?)\n") 

 

 

# Expose this plugin's implementation for the pytest_assertrepr_compare hook 

pytest_assertrepr_compare = util.assertrepr_compare