Hide keyboard shortcuts

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

1import os 

2import platform 

3import sys 

4import traceback 

5 

6from ..outcomes import fail 

7from ..outcomes import TEST_OUTCOME 

8 

9 

10def cached_eval(config, expr, d): 

11 if not hasattr(config, "_evalcache"): 

12 config._evalcache = {} 

13 try: 

14 return config._evalcache[expr] 

15 except KeyError: 

16 import _pytest._code 

17 

18 exprcode = _pytest._code.compile(expr, mode="eval") 

19 config._evalcache[expr] = x = eval(exprcode, d) 

20 return x 

21 

22 

23class MarkEvaluator: 

24 def __init__(self, item, name): 

25 self.item = item 

26 self._marks = None 

27 self._mark = None 

28 self._mark_name = name 

29 

30 def __bool__(self): 

31 # don't cache here to prevent staleness 

32 return bool(self._get_marks()) 

33 

34 __nonzero__ = __bool__ 

35 

36 def wasvalid(self): 

37 return not hasattr(self, "exc") 

38 

39 def _get_marks(self): 

40 return list(self.item.iter_markers(name=self._mark_name)) 

41 

42 def invalidraise(self, exc): 

43 raises = self.get("raises") 

44 if not raises: 

45 return 

46 return not isinstance(exc, raises) 

47 

48 def istrue(self): 

49 try: 

50 return self._istrue() 

51 except TEST_OUTCOME: 

52 self.exc = sys.exc_info() 

53 if isinstance(self.exc[1], SyntaxError): 

54 # TODO: Investigate why SyntaxError.offset is Optional, and if it can be None here. 

55 assert self.exc[1].offset is not None 

56 msg = [" " * (self.exc[1].offset + 4) + "^"] 

57 msg.append("SyntaxError: invalid syntax") 

58 else: 

59 msg = traceback.format_exception_only(*self.exc[:2]) 

60 fail( 

61 "Error evaluating %r expression\n" 

62 " %s\n" 

63 "%s" % (self._mark_name, self.expr, "\n".join(msg)), 

64 pytrace=False, 

65 ) 

66 

67 def _getglobals(self): 

68 d = {"os": os, "sys": sys, "platform": platform, "config": self.item.config} 

69 if hasattr(self.item, "obj"): 

70 d.update(self.item.obj.__globals__) 

71 return d 

72 

73 def _istrue(self): 

74 if hasattr(self, "result"): 

75 return self.result 

76 self._marks = self._get_marks() 

77 

78 if self._marks: 

79 self.result = False 

80 for mark in self._marks: 

81 self._mark = mark 

82 if "condition" in mark.kwargs: 

83 args = (mark.kwargs["condition"],) 

84 else: 

85 args = mark.args 

86 

87 for expr in args: 

88 self.expr = expr 

89 if isinstance(expr, str): 

90 d = self._getglobals() 

91 result = cached_eval(self.item.config, expr, d) 

92 else: 

93 if "reason" not in mark.kwargs: 

94 # XXX better be checked at collection time 

95 msg = ( 

96 "you need to specify reason=STRING " 

97 "when using booleans as conditions." 

98 ) 

99 fail(msg) 

100 result = bool(expr) 

101 if result: 

102 self.result = True 

103 self.reason = mark.kwargs.get("reason", None) 

104 self.expr = expr 

105 return self.result 

106 

107 if not args: 

108 self.result = True 

109 self.reason = mark.kwargs.get("reason", None) 

110 return self.result 

111 return False 

112 

113 def get(self, attr, default=None): 

114 if self._mark is None: 

115 return default 

116 return self._mark.kwargs.get(attr, default) 

117 

118 def getexplanation(self): 

119 expl = getattr(self, "reason", None) or self.get("reason", None) 

120 if not expl: 

121 if not hasattr(self, "expr"): 

122 return "" 

123 else: 

124 return "condition: " + str(self.expr) 

125 return expl