assertions.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import contextlib
  2. import re
  3. import sys
  4. def eq_(a, b, msg=None):
  5. """Assert a == b, with repr messaging on failure."""
  6. assert a == b, msg or "%r != %r" % (a, b)
  7. def ne_(a, b, msg=None):
  8. """Assert a != b, with repr messaging on failure."""
  9. assert a != b, msg or "%r == %r" % (a, b)
  10. def in_(a, b, msg=None):
  11. """Assert a in b, with repr messaging on failure."""
  12. assert a in b, msg or "%r not in %r" % (a, b)
  13. def not_in(a, b, msg=None):
  14. """Assert a in not b, with repr messaging on failure."""
  15. assert a not in b, msg or "%r is in %r" % (a, b)
  16. def _assert_proper_exception_context(exception):
  17. """assert that any exception we're catching does not have a __context__
  18. without a __cause__, and that __suppress_context__ is never set.
  19. Python 3 will report nested as exceptions as "during the handling of
  20. error X, error Y occurred". That's not what we want to do. We want
  21. these exceptions in a cause chain.
  22. """
  23. if (
  24. exception.__context__ is not exception.__cause__
  25. and not exception.__suppress_context__
  26. ):
  27. assert False, (
  28. "Exception %r was correctly raised but did not set a cause, "
  29. "within context %r as its cause."
  30. % (exception, exception.__context__)
  31. )
  32. def _assert_proper_cause_cls(exception, cause_cls):
  33. """assert that any exception we're catching does not have a __context__
  34. without a __cause__, and that __suppress_context__ is never set.
  35. Python 3 will report nested as exceptions as "during the handling of
  36. error X, error Y occurred". That's not what we want to do. We want
  37. these exceptions in a cause chain.
  38. """
  39. assert isinstance(exception.__cause__, cause_cls), (
  40. "Exception %r was correctly raised but has cause %r, which does not "
  41. "have the expected cause type %r."
  42. % (exception, exception.__cause__, cause_cls)
  43. )
  44. def assert_raises(except_cls, callable_, *args, **kw):
  45. return _assert_raises(except_cls, callable_, args, kw)
  46. def assert_raises_with_proper_context(except_cls, callable_, *args, **kw):
  47. return _assert_raises(except_cls, callable_, args, kw, check_context=True)
  48. def assert_raises_with_given_cause(
  49. except_cls, cause_cls, callable_, *args, **kw
  50. ):
  51. return _assert_raises(except_cls, callable_, args, kw, cause_cls=cause_cls)
  52. def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
  53. return _assert_raises(except_cls, callable_, args, kwargs, msg=msg)
  54. def assert_raises_message_with_proper_context(
  55. except_cls, msg, callable_, *args, **kwargs
  56. ):
  57. return _assert_raises(
  58. except_cls, callable_, args, kwargs, msg=msg, check_context=True
  59. )
  60. def assert_raises_message_with_given_cause(
  61. except_cls, msg, cause_cls, callable_, *args, **kwargs
  62. ):
  63. return _assert_raises(
  64. except_cls, callable_, args, kwargs, msg=msg, cause_cls=cause_cls
  65. )
  66. def _assert_raises(
  67. except_cls,
  68. callable_,
  69. args,
  70. kwargs,
  71. msg=None,
  72. check_context=False,
  73. cause_cls=None,
  74. ):
  75. with _expect_raises(except_cls, msg, check_context, cause_cls) as ec:
  76. callable_(*args, **kwargs)
  77. return ec.error
  78. class _ErrorContainer:
  79. error = None
  80. @contextlib.contextmanager
  81. def _expect_raises(except_cls, msg=None, check_context=False, cause_cls=None):
  82. ec = _ErrorContainer()
  83. if check_context:
  84. are_we_already_in_a_traceback = sys.exc_info()[0]
  85. try:
  86. yield ec
  87. success = False
  88. except except_cls as err:
  89. ec.error = err
  90. success = True
  91. if msg is not None:
  92. # I'm often pdbing here, and "err" above isn't
  93. # in scope, so assign the string explicitly
  94. error_as_string = str(err)
  95. assert re.search(msg, error_as_string, re.UNICODE), "%r !~ %s" % (
  96. msg,
  97. error_as_string,
  98. )
  99. if cause_cls is not None:
  100. _assert_proper_cause_cls(err, cause_cls)
  101. if check_context and not are_we_already_in_a_traceback:
  102. _assert_proper_exception_context(err)
  103. print(str(err).encode("utf-8"))
  104. # it's generally a good idea to not carry traceback objects outside
  105. # of the except: block, but in this case especially we seem to have
  106. # hit some bug in either python 3.10.0b2 or greenlet or both which
  107. # this seems to fix:
  108. # https://github.com/python-greenlet/greenlet/issues/242
  109. del ec
  110. # assert outside the block so it works for AssertionError too !
  111. assert success, "Callable did not raise an exception"
  112. def expect_raises(except_cls, check_context=False):
  113. return _expect_raises(except_cls, check_context=check_context)
  114. def expect_raises_message(except_cls, msg, check_context=False):
  115. return _expect_raises(except_cls, msg=msg, check_context=check_context)
  116. def expect_raises_with_proper_context(except_cls, check_context=True):
  117. return _expect_raises(except_cls, check_context=check_context)
  118. def expect_raises_message_with_proper_context(
  119. except_cls, msg, check_context=True
  120. ):
  121. return _expect_raises(except_cls, msg=msg, check_context=check_context)