Repository: HypothesisWorks/hypothesis

Issue:
target() cannot be called outside a hypothesis test
I have a test `assert_allclose_phase` which checks whether two arrays are almost equal modulo one. When running under `hypothesis`, this seems like a sensible place to put `target`, albeit with a(n optional?) `name` argument in case I want to call it twice in the same test. Unfortunately if I do so then I can't use the same function in non-`hypothesis` tests:

```
E           hypothesis.errors.InvalidArgument: Calling target() outside of a test is invalid.
```

My current workaround is to call `target` only if `name` is provided, but this clutters simple tests that wouldn't need a `name`.


Discussion:
https://github.com/HypothesisWorks/hypothesis/blob/caccabbd107757ee6e9de1de7e3cff5474d9ccc5/hypothesis-python/src/hypothesis/control.py#L173-L175

I'd be happy to add an optional keyword-only argument like `allow_outside_given_test=False`, and if `True` add an early return inside this check.  The idea would be to continue catching accidental use outside `@given`-tests, which would be a noop, but also support this kind of helper function which may be used outside them too. 
I'll give this a shot, if no one has yet
Thanks to @PiDelport in https://github.com/HypothesisWorks/hypothesis/pull/2675#pullrequestreview-547298906, I've come around to the idea that instead of a special-case argument, we should provide a general predicate.  For example:

```python
if hypothesis.currently_in_test_context():
    # We're in an @given or stateful test, and can safely use e.g.
    target(x)
    event("hello")
    assume(foo)
    if bar: reject()
```

This should still be documented as "only use this in test helper functions", but I think it would be of sufficient if niche interest to justify its existence - and more orthogonal/composable than supporting similar capabilities on a function-by-function basis.

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/__init__.py b/hypothesis-python/src/hypothesis/__init__.py
--- a/hypothesis-python/src/hypothesis/__init__.py
+++ b/hypothesis-python/src/hypothesis/__init__.py
@@ -22,7 +22,14 @@
 
 import hypothesis._error_if_old  # noqa  # imported for side-effect of nice error
 from hypothesis._settings import HealthCheck, Phase, Verbosity, settings
-from hypothesis.control import assume, event, note, reject, target
+from hypothesis.control import (
+    assume,
+    currently_in_test_context,
+    event,
+    note,
+    reject,
+    target,
+)
 from hypothesis.core import example, find, given, reproduce_failure, seed
 from hypothesis.entry_points import run
 from hypothesis.internal.entropy import register_random
@@ -30,21 +37,22 @@
 from hypothesis.version import __version__, __version_info__
 
 __all__ = [
-    "settings",
-    "Verbosity",
     "HealthCheck",
     "Phase",
+    "Verbosity",
     "assume",
-    "reject",
-    "seed",
-    "given",
-    "reproduce_failure",
-    "find",
-    "example",
-    "note",
+    "currently_in_test_context",
     "event",
+    "example",
+    "find",
+    "given",
     "infer",
+    "note",
     "register_random",
+    "reject",
+    "reproduce_failure",
+    "seed",
+    "settings",
     "target",
     "__version__",
     "__version_info__",
diff --git a/hypothesis-python/src/hypothesis/control.py b/hypothesis-python/src/hypothesis/control.py
--- a/hypothesis-python/src/hypothesis/control.py
+++ b/hypothesis-python/src/hypothesis/control.py
@@ -45,6 +45,18 @@ def assume(condition: object) -> bool:
 _current_build_context = DynamicVariable(None)
 
 
+def currently_in_test_context() -> bool:
+    """Return ``True`` if the calling code is currently running inside an
+    :func:`@given <hypothesis.given>` or :doc:`stateful <stateful>` test,
+    ``False`` otherwise.
+
+    This is useful for third-party integrations and assertion helpers which
+    may be called from traditional or property-based tests, but can only use
+    :func:`~hypothesis.assume` or :func:`~hypothesis.target` in the latter case.
+    """
+    return _current_build_context.value is not None
+
+
 def current_build_context():
     context = _current_build_context.value
     if context is None:


Test patch:
diff --git a/hypothesis-python/tests/cover/test_control.py b/hypothesis-python/tests/cover/test_control.py
--- a/hypothesis-python/tests/cover/test_control.py
+++ b/hypothesis-python/tests/cover/test_control.py
@@ -21,11 +21,13 @@
     _current_build_context,
     cleanup,
     current_build_context,
+    currently_in_test_context,
     event,
     note,
 )
 from hypothesis.errors import CleanupFailed, InvalidArgument
 from hypothesis.internal.conjecture.data import ConjectureData as TD
+from hypothesis.stateful import RuleBasedStateMachine, rule
 from hypothesis.strategies import integers
 from tests.common.utils import capture_out
 
@@ -156,3 +158,21 @@ def test(x):
     v = out.getvalue()
     for x in sorted(messages):
         assert x in v
+
+
+def test_not_currently_in_hypothesis():
+    assert currently_in_test_context() is False
+
+
+@given(integers())
+def test_currently_in_hypothesis(_):
+    assert currently_in_test_context() is True
+
+
+class ContextMachine(RuleBasedStateMachine):
+    @rule()
+    def step(self):
+        assert currently_in_test_context() is True
+
+
+test_currently_in_stateful_test = ContextMachine.TestCase