Repository: HypothesisWorks/hypothesis

Issue:
RFC: Undeprecating `find`
We've marked `find` as deprecated because, I quote:

```
`find(s, f)` is deprecated, because it is rarely used but takes ongoing work to maintain as we upgrade other parts of Hypothesis.
```

I don't think either of these parts are really true.

1.  `find` has a smallish base of users, but they're quite enthusiastic about it. It's probably used more often than, say, `hypothesis.extra.django`, or many of our niche strategies.
2. It really doesn't involve much maintenance at all. #2235 is pretty much the first time we've touched it in years other than to deprecate it, and it will reduce any future maintenance even further because it's now using exactly the same machinery as everything else.

Against that, find is a convenient tool for exploring Hypothesis, and is often useful in its own right, and having to rephrase interactive experimentation with it in terms of given style tests is kinda a pain.


Discussion:
Fine by me - I think the case for deprecation was basically that it was too different from `@given` for both maintainers and users (i.e. had a different data distribution), which is clearly no longer true :smile: 

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/core.py b/hypothesis-python/src/hypothesis/core.py
--- a/hypothesis-python/src/hypothesis/core.py
+++ b/hypothesis-python/src/hypothesis/core.py
@@ -40,7 +40,6 @@
     Phase,
     Verbosity,
     local_settings,
-    note_deprecation,
     settings as Settings,
 )
 from hypothesis.control import BuildContext
@@ -1094,11 +1093,6 @@ def find(
     # type: (...) -> Any
     """Returns the minimal example from the given strategy ``specifier`` that
     matches the predicate function ``condition``."""
-    note_deprecation(
-        "`find(s, f)` is deprecated, because it is rarely used but takes "
-        "ongoing work to maintain as we upgrade other parts of Hypothesis.",
-        since="2019-07-11",
-    )
     if settings is None:
         settings = Settings(max_examples=2000)
     settings = Settings(settings, suppress_health_check=HealthCheck.all())


Test patch:
diff --git a/hypothesis-python/tests/cover/test_arbitrary_data.py b/hypothesis-python/tests/cover/test_arbitrary_data.py
--- a/hypothesis-python/tests/cover/test_arbitrary_data.py
+++ b/hypothesis-python/tests/cover/test_arbitrary_data.py
@@ -21,7 +21,7 @@
 
 from hypothesis import find, given, reporting, strategies as st
 from hypothesis.errors import InvalidArgument
-from tests.common.utils import capture_out, checks_deprecated_behaviour, raises
+from tests.common.utils import capture_out, raises
 
 
 @given(st.integers(), st.data())
@@ -82,7 +82,6 @@ def test(data1, data2):
     assert "Draw 2: 0" in result
 
 
-@checks_deprecated_behaviour
 def test_errors_when_used_in_find():
     with raises(InvalidArgument):
         find(st.data(), lambda x: x.draw(st.booleans()))
diff --git a/hypothesis-python/tests/cover/test_core.py b/hypothesis-python/tests/cover/test_core.py
--- a/hypothesis-python/tests/cover/test_core.py
+++ b/hypothesis-python/tests/cover/test_core.py
@@ -23,10 +23,8 @@
 import hypothesis.strategies as s
 from hypothesis import find, given, reject, settings
 from hypothesis.errors import InvalidArgument, NoSuchExample, Unsatisfiable
-from tests.common.utils import checks_deprecated_behaviour
 
 
-@checks_deprecated_behaviour
 def test_stops_after_max_examples_if_satisfying():
     tracker = []
 
@@ -42,7 +40,6 @@ def track(x):
     assert len(tracker) == max_examples
 
 
-@checks_deprecated_behaviour
 def test_stops_after_ten_times_max_examples_if_not_satisfying():
     count = [0]
 
@@ -104,12 +101,10 @@ def inner(x):
     assert len([x for x in values if x > 100]) == 1
 
 
-@checks_deprecated_behaviour
 def test_can_find_with_db_eq_none():
     find(s.integers(), bool, settings(database=None, max_examples=100))
 
 
-@checks_deprecated_behaviour
 def test_no_such_example():
     with pytest.raises(NoSuchExample):
         find(s.none(), bool, database_key=b"no such example")
diff --git a/hypothesis-python/tests/cover/test_random_module.py b/hypothesis-python/tests/cover/test_random_module.py
--- a/hypothesis-python/tests/cover/test_random_module.py
+++ b/hypothesis-python/tests/cover/test_random_module.py
@@ -26,7 +26,7 @@
 from hypothesis.errors import InvalidArgument
 from hypothesis.internal import entropy
 from hypothesis.internal.entropy import deterministic_PRNG
-from tests.common.utils import capture_out, checks_deprecated_behaviour
+from tests.common.utils import capture_out
 
 
 def test_can_seed_random():
@@ -131,7 +131,6 @@ def test(r):
         assert state_a != state_b
 
 
-@checks_deprecated_behaviour
 def test_find_does_not_pollute_state():
     with deterministic_PRNG():
 
diff --git a/hypothesis-python/tests/cover/test_runner_strategy.py b/hypothesis-python/tests/cover/test_runner_strategy.py
--- a/hypothesis-python/tests/cover/test_runner_strategy.py
+++ b/hypothesis-python/tests/cover/test_runner_strategy.py
@@ -24,7 +24,6 @@
 from hypothesis import find, given, strategies as st
 from hypothesis.errors import InvalidArgument
 from hypothesis.stateful import RuleBasedStateMachine, rule
-from tests.common.utils import checks_deprecated_behaviour
 
 
 def test_cannot_use_without_a_runner():
@@ -36,13 +35,11 @@ def f(x):
         f()
 
 
-@checks_deprecated_behaviour
 def test_cannot_use_in_find_without_default():
     with pytest.raises(InvalidArgument):
         find(st.runner(), lambda x: True)
 
 
-@checks_deprecated_behaviour
 def test_is_default_in_find():
     t = object()
     assert find(st.runner(t), lambda x: True) == t
diff --git a/hypothesis-python/tests/cover/test_validation.py b/hypothesis-python/tests/cover/test_validation.py
--- a/hypothesis-python/tests/cover/test_validation.py
+++ b/hypothesis-python/tests/cover/test_validation.py
@@ -225,7 +225,6 @@ def test(x, y):
         test()
 
 
-@checks_deprecated_behaviour
 def test_cannot_find_non_strategies():
     with pytest.raises(InvalidArgument):
         find(bool, bool)
diff --git a/hypothesis-python/tests/cover/test_verbosity.py b/hypothesis-python/tests/cover/test_verbosity.py
--- a/hypothesis-python/tests/cover/test_verbosity.py
+++ b/hypothesis-python/tests/cover/test_verbosity.py
@@ -24,7 +24,7 @@
 from hypothesis.reporting import default as default_reporter, with_reporter
 from hypothesis.strategies import booleans, integers, lists
 from tests.common.debug import minimal
-from tests.common.utils import capture_out, checks_deprecated_behaviour, fails
+from tests.common.utils import capture_out, fails
 
 
 @contextmanager
@@ -72,7 +72,6 @@ def test_includes_progress_in_verbose_mode():
     assert u"Falsifying example: " in out
 
 
-@checks_deprecated_behaviour
 def test_prints_initial_attempts_on_find():
 
     with capture_verbosity() as o:
diff --git a/hypothesis-python/tests/nocover/test_database_usage.py b/hypothesis-python/tests/nocover/test_database_usage.py
--- a/hypothesis-python/tests/nocover/test_database_usage.py
+++ b/hypothesis-python/tests/nocover/test_database_usage.py
@@ -24,18 +24,13 @@
 from hypothesis.database import ExampleDatabase, InMemoryExampleDatabase
 from hypothesis.errors import NoSuchExample, Unsatisfiable
 from hypothesis.internal.compat import hbytes
-from tests.common.utils import (
-    all_values,
-    checks_deprecated_behaviour,
-    non_covering_examples,
-)
+from tests.common.utils import all_values, non_covering_examples
 
 
 def has_a_non_zero_byte(x):
     return any(hbytes(x))
 
 
-@checks_deprecated_behaviour
 def test_saves_incremental_steps_in_database():
     key = b"a database key"
     database = InMemoryExampleDatabase()
@@ -48,7 +43,6 @@ def test_saves_incremental_steps_in_database():
     assert len(all_values(database)) > 1
 
 
-@checks_deprecated_behaviour
 def test_clears_out_database_as_things_get_boring():
     key = b"a database key"
     database = InMemoryExampleDatabase()
@@ -81,7 +75,6 @@ def stuff():
         assert False
 
 
-@checks_deprecated_behaviour
 def test_trashes_invalid_examples():
     key = b"a database key"
     database = InMemoryExampleDatabase()
@@ -106,7 +99,6 @@ def stuff():
     assert len(all_values(database)) < original
 
 
-@checks_deprecated_behaviour
 def test_respects_max_examples_in_database_usage():
     key = b"a database key"
     database = InMemoryExampleDatabase()
diff --git a/hypothesis-python/tests/nocover/test_deferred_errors.py b/hypothesis-python/tests/nocover/test_deferred_errors.py
--- a/hypothesis-python/tests/nocover/test_deferred_errors.py
+++ b/hypothesis-python/tests/nocover/test_deferred_errors.py
@@ -23,7 +23,6 @@
 from hypothesis import find, given
 from hypothesis._strategies import defines_strategy
 from hypothesis.errors import InvalidArgument
-from tests.common.utils import checks_deprecated_behaviour
 
 
 def test_does_not_error_on_initial_calculation():
@@ -50,7 +49,6 @@ def test(x):
         test()
 
 
-@checks_deprecated_behaviour
 def test_errors_on_find():
     s = st.lists(st.integers(), min_size=5, max_size=2)
     with pytest.raises(InvalidArgument):
diff --git a/hypothesis-python/tests/nocover/test_find.py b/hypothesis-python/tests/nocover/test_find.py
--- a/hypothesis-python/tests/nocover/test_find.py
+++ b/hypothesis-python/tests/nocover/test_find.py
@@ -25,7 +25,6 @@
 from hypothesis.errors import NoSuchExample
 from hypothesis.strategies import booleans, dictionaries, floats, integers, lists
 from tests.common.debug import minimal
-from tests.common.utils import checks_deprecated_behaviour
 
 
 def test_can_find_an_int():
@@ -50,7 +49,6 @@ def test_can_find_nans():
         assert 2 <= len(x) <= 3
 
 
-@checks_deprecated_behaviour
 def test_condition_is_name():
     settings = Settings(max_examples=20)
     with pytest.raises(NoSuchExample) as e:
diff --git a/hypothesis-python/tests/nocover/test_randomization.py b/hypothesis-python/tests/nocover/test_randomization.py
--- a/hypothesis-python/tests/nocover/test_randomization.py
+++ b/hypothesis-python/tests/nocover/test_randomization.py
@@ -23,10 +23,9 @@
 
 import hypothesis.strategies as st
 from hypothesis import Verbosity, find, given, settings
-from tests.common.utils import checks_deprecated_behaviour, no_shrink
+from tests.common.utils import no_shrink
 
 
-@checks_deprecated_behaviour
 def test_seeds_off_random():
     s = settings(phases=no_shrink, database=None)
     r = random.getstate()