control.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # coding=utf-8
  2. #
  3. # This file is part of Hypothesis, which may be found at
  4. # https://github.com/HypothesisWorks/hypothesis-python
  5. #
  6. # Most of this work is copyright (C) 2013-2018 David R. MacIver
  7. # (david@drmaciver.com), but it contains contributions by others. See
  8. # CONTRIBUTING.rst for a full list of people who may hold copyright, and
  9. # consult the git log if you need to determine who owns an individual
  10. # contribution.
  11. #
  12. # This Source Code Form is subject to the terms of the Mozilla Public License,
  13. # v. 2.0. If a copy of the MPL was not distributed with this file, You can
  14. # obtain one at http://mozilla.org/MPL/2.0/.
  15. #
  16. # END HEADER
  17. from __future__ import division, print_function, absolute_import
  18. import traceback
  19. from hypothesis.errors import CleanupFailed, InvalidArgument, \
  20. UnsatisfiedAssumption
  21. from hypothesis.reporting import report
  22. from hypothesis.utils.dynamicvariables import DynamicVariable
  23. def reject():
  24. raise UnsatisfiedAssumption()
  25. def assume(condition):
  26. """``assume()`` is like an :ref:`assert <python:assert>` that marks the
  27. example as bad, rather than failing the test.
  28. This allows you to specify properties that you *assume* will be
  29. true, and let Hypothesis try to avoid similar examples in future.
  30. """
  31. if not condition:
  32. raise UnsatisfiedAssumption()
  33. return True
  34. _current_build_context = DynamicVariable(None)
  35. def current_build_context():
  36. context = _current_build_context.value
  37. if context is None:
  38. raise InvalidArgument(
  39. u'No build context registered')
  40. return context
  41. class BuildContext(object):
  42. def __init__(self, data, is_final=False, close_on_capture=True):
  43. self.data = data
  44. self.tasks = []
  45. self.is_final = is_final
  46. self.close_on_capture = close_on_capture
  47. self.close_on_del = False
  48. self.notes = []
  49. def __enter__(self):
  50. self.assign_variable = _current_build_context.with_value(self)
  51. self.assign_variable.__enter__()
  52. return self
  53. def __exit__(self, exc_type, exc_value, tb):
  54. self.assign_variable.__exit__(exc_type, exc_value, tb)
  55. if self.close() and exc_type is None:
  56. raise CleanupFailed()
  57. def local(self):
  58. return _current_build_context.with_value(self)
  59. def close(self):
  60. any_failed = False
  61. for task in self.tasks:
  62. try:
  63. task()
  64. except BaseException:
  65. any_failed = True
  66. report(traceback.format_exc())
  67. return any_failed
  68. def cleanup(teardown):
  69. """Register a function to be called when the current test has finished
  70. executing. Any exceptions thrown in teardown will be printed but not
  71. rethrown.
  72. Inside a test this isn't very interesting, because you can just use
  73. a finally block, but note that you can use this inside map, flatmap,
  74. etc. in order to e.g. insist that a value is closed at the end.
  75. """
  76. context = _current_build_context.value
  77. if context is None:
  78. raise InvalidArgument(
  79. u'Cannot register cleanup outside of build context')
  80. context.tasks.append(teardown)
  81. def note(value):
  82. """Report this value in the final execution."""
  83. context = _current_build_context.value
  84. if context is None:
  85. raise InvalidArgument(
  86. 'Cannot make notes outside of a test')
  87. context.notes.append(value)
  88. if context.is_final:
  89. report(value)
  90. def event(value):
  91. """Record an event that occurred this test. Statistics on number of test
  92. runs with each event will be reported at the end if you run Hypothesis in
  93. statistics reporting mode.
  94. Events should be strings or convertible to them.
  95. """
  96. context = _current_build_context.value
  97. if context is None:
  98. raise InvalidArgument(
  99. 'Cannot make record events outside of a test')
  100. if context.data is not None:
  101. context.data.note_event(value)