evaluate.py 3.8 KB

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