assertion.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import sys
  2. import py
  3. BuiltinAssertionError = py.builtin.builtins.AssertionError
  4. _reprcompare = None # if set, will be called by assert reinterp for comparison ops
  5. def _format_explanation(explanation):
  6. """This formats an explanation
  7. Normally all embedded newlines are escaped, however there are
  8. three exceptions: \n{, \n} and \n~. The first two are intended
  9. cover nested explanations, see function and attribute explanations
  10. for examples (.visit_Call(), visit_Attribute()). The last one is
  11. for when one explanation needs to span multiple lines, e.g. when
  12. displaying diffs.
  13. """
  14. raw_lines = (explanation or '').split('\n')
  15. # escape newlines not followed by {, } and ~
  16. lines = [raw_lines[0]]
  17. for l in raw_lines[1:]:
  18. if l.startswith('{') or l.startswith('}') or l.startswith('~'):
  19. lines.append(l)
  20. else:
  21. lines[-1] += '\\n' + l
  22. result = lines[:1]
  23. stack = [0]
  24. stackcnt = [0]
  25. for line in lines[1:]:
  26. if line.startswith('{'):
  27. if stackcnt[-1]:
  28. s = 'and '
  29. else:
  30. s = 'where '
  31. stack.append(len(result))
  32. stackcnt[-1] += 1
  33. stackcnt.append(0)
  34. result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
  35. elif line.startswith('}'):
  36. assert line.startswith('}')
  37. stack.pop()
  38. stackcnt.pop()
  39. result[stack[-1]] += line[1:]
  40. else:
  41. assert line.startswith('~')
  42. result.append(' '*len(stack) + line[1:])
  43. assert len(stack) == 1
  44. return '\n'.join(result)
  45. class AssertionError(BuiltinAssertionError):
  46. def __init__(self, *args):
  47. BuiltinAssertionError.__init__(self, *args)
  48. if args:
  49. try:
  50. self.msg = str(args[0])
  51. except py.builtin._sysex:
  52. raise
  53. except:
  54. self.msg = "<[broken __repr__] %s at %0xd>" %(
  55. args[0].__class__, id(args[0]))
  56. else:
  57. f = py.code.Frame(sys._getframe(1))
  58. try:
  59. source = f.code.fullsource
  60. if source is not None:
  61. try:
  62. source = source.getstatement(f.lineno, assertion=True)
  63. except IndexError:
  64. source = None
  65. else:
  66. source = str(source.deindent()).strip()
  67. except py.error.ENOENT:
  68. source = None
  69. # this can also occur during reinterpretation, when the
  70. # co_filename is set to "<run>".
  71. if source:
  72. self.msg = reinterpret(source, f, should_fail=True)
  73. else:
  74. self.msg = "<could not determine information>"
  75. if not self.args:
  76. self.args = (self.msg,)
  77. if sys.version_info > (3, 0):
  78. AssertionError.__module__ = "builtins"
  79. reinterpret_old = "old reinterpretation not available for py3"
  80. else:
  81. from py._code._assertionold import interpret as reinterpret_old
  82. from py._code._assertionnew import interpret as reinterpret