evaluate.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import os
  2. import six
  3. import sys
  4. import platform
  5. import traceback
  6. from ..outcomes import fail, TEST_OUTCOME
  7. def cached_eval(config, expr, d):
  8. if not hasattr(config, "_evalcache"):
  9. config._evalcache = {}
  10. try:
  11. return config._evalcache[expr]
  12. except KeyError:
  13. import _pytest._code
  14. exprcode = _pytest._code.compile(expr, mode="eval")
  15. config._evalcache[expr] = x = eval(exprcode, d)
  16. return x
  17. class MarkEvaluator(object):
  18. def __init__(self, item, name):
  19. self.item = item
  20. self._marks = None
  21. self._mark = None
  22. self._mark_name = name
  23. def __bool__(self):
  24. # dont cache here to prevent staleness
  25. return bool(self._get_marks())
  26. __nonzero__ = __bool__
  27. def wasvalid(self):
  28. return not hasattr(self, "exc")
  29. def _get_marks(self):
  30. return list(self.item.iter_markers(name=self._mark_name))
  31. def invalidraise(self, exc):
  32. raises = self.get("raises")
  33. if not raises:
  34. return
  35. return not isinstance(exc, raises)
  36. def istrue(self):
  37. try:
  38. return self._istrue()
  39. except TEST_OUTCOME:
  40. self.exc = sys.exc_info()
  41. if isinstance(self.exc[1], SyntaxError):
  42. msg = [" " * (self.exc[1].offset + 4) + "^"]
  43. msg.append("SyntaxError: invalid syntax")
  44. else:
  45. msg = traceback.format_exception_only(*self.exc[:2])
  46. fail(
  47. "Error evaluating %r expression\n"
  48. " %s\n"
  49. "%s" % (self._mark_name, self.expr, "\n".join(msg)),
  50. pytrace=False,
  51. )
  52. def _getglobals(self):
  53. d = {"os": os, "sys": sys, "platform": platform, "config": self.item.config}
  54. if hasattr(self.item, "obj"):
  55. d.update(self.item.obj.__globals__)
  56. return d
  57. def _istrue(self):
  58. if hasattr(self, "result"):
  59. return self.result
  60. self._marks = self._get_marks()
  61. if self._marks:
  62. self.result = False
  63. for mark in self._marks:
  64. self._mark = mark
  65. if "condition" in mark.kwargs:
  66. args = (mark.kwargs["condition"],)
  67. else:
  68. args = mark.args
  69. for expr in args:
  70. self.expr = expr
  71. if isinstance(expr, six.string_types):
  72. d = self._getglobals()
  73. result = cached_eval(self.item.config, expr, d)
  74. else:
  75. if "reason" not in mark.kwargs:
  76. # XXX better be checked at collection time
  77. msg = "you need to specify reason=STRING " "when using booleans as conditions."
  78. fail(msg)
  79. result = bool(expr)
  80. if result:
  81. self.result = True
  82. self.reason = mark.kwargs.get("reason", None)
  83. self.expr = expr
  84. return self.result
  85. if not args:
  86. self.result = True
  87. self.reason = mark.kwargs.get("reason", None)
  88. return self.result
  89. return False
  90. def get(self, attr, default=None):
  91. if self._mark is None:
  92. return default
  93. return self._mark.kwargs.get(attr, default)
  94. def getexplanation(self):
  95. expl = getattr(self, "reason", None) or self.get("reason", None)
  96. if not expl:
  97. if not hasattr(self, "expr"):
  98. return ""
  99. else:
  100. return "condition: " + str(self.expr)
  101. return expl