Repository: HypothesisWorks/hypothesis

Issue:
More clear behavior when passing non-strings to `note`
Consider the following:

```python
from hypothesis import *
from hypothesis.strategies import *

class A:
    pass

@given(builds(A))
def f(a):
    note(a)
    assert False

f()
```

This errors with the following traceback:
```python
Traceback (most recent call last):
  File "/Users/tybug/Desktop/Liam/coding/sandbox/__hypothesis.py", line 12, in <module>
    f()
  File "/Users/tybug/Desktop/Liam/coding/sandbox/__hypothesis.py", line 8, in f
    def f(a):
              
  File "/Users/tybug/Desktop/Liam/coding/hypothesis/hypothesis-python/src/hypothesis/core.py", line 1599, in wrapped_test
    raise the_error_hypothesis_found
  File "/Users/tybug/Desktop/Liam/coding/hypothesis/hypothesis-python/src/hypothesis/core.py", line 1566, in wrapped_test
    state.run_engine()
  File "/Users/tybug/Desktop/Liam/coding/hypothesis/hypothesis-python/src/hypothesis/core.py", line 1214, in run_engine
    _raise_to_user(errors_to_report, self.settings, report_lines)
  File "/Users/tybug/Desktop/Liam/coding/hypothesis/hypothesis-python/src/hypothesis/core.py", line 1223, in _raise_to_user
    add_note(err, note)
  File "/Users/tybug/Desktop/Liam/coding/hypothesis/hypothesis-python/src/hypothesis/internal/compat.py", line 50, in add_note
    exc.add_note(note)
TypeError: note must be a str, not 'A'
```

In this case, it's relatively obvious that the `note` call is the culprit. But it may be more confusing in more complicated functions, since that information gets trimmed from the traceback.

I propose we either:
- fail early and explicitly when `note` is passed a non-str value
- automatically coerce `note(x)` to `report(str(x))` instead of `report(x)`

This came up in practice in https://github.com/HypothesisWorks/hypothesis/blob/798803ab0546ec08862d6f3f50cc70f43ab12a8b/hypothesis-python/tests/nocover/test_explore_arbitrary_languages.py#L115-L120 (fix to come!)


Discussion:
Let's use our pretty-printer for non-strings, to give a maximally-informative string form.  Thanks for spotting this!

Resolving patch:
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
@@ -23,7 +23,7 @@
 from hypothesis.internal.validation import check_type
 from hypothesis.reporting import report, verbose_report
 from hypothesis.utils.dynamicvariables import DynamicVariable
-from hypothesis.vendor.pretty import IDKey
+from hypothesis.vendor.pretty import IDKey, pretty
 
 
 def _calling_function_name(frame):
@@ -170,9 +170,11 @@ def should_note():
     return context.is_final or settings.default.verbosity >= Verbosity.verbose
 
 
-def note(value: str) -> None:
+def note(value: object) -> None:
     """Report this value for the minimal failing example."""
     if should_note():
+        if not isinstance(value, str):
+            value = pretty(value)
         report(value)
 
 


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
@@ -8,6 +8,8 @@
 # v. 2.0. If a copy of the MPL was not distributed with this file, You can
 # obtain one at https://mozilla.org/MPL/2.0/.
 
+from dataclasses import dataclass
+
 import pytest
 
 from hypothesis import Verbosity, assume, given, reject, reporting, settings
@@ -176,6 +178,26 @@ def test(x):
         assert x in v
 
 
+@dataclass
+class CanBePrettyPrinted:
+    n: int
+
+
+def test_note_pretty_prints():
+    reports = []
+
+    @given(integers(1, 10))
+    def test(n):
+        with reporting.with_reporter(reports.append):
+            note(CanBePrettyPrinted(n))
+        assert n != 5
+
+    with pytest.raises(AssertionError):
+        test()
+
+    assert reports == ["CanBePrettyPrinted(n=5)"]
+
+
 def test_not_currently_in_hypothesis():
     assert currently_in_test_context() is False
 
diff --git a/hypothesis-python/tests/ghostwriter/recorded/hypothesis_module_magic.txt b/hypothesis-python/tests/ghostwriter/recorded/hypothesis_module_magic.txt
--- a/hypothesis-python/tests/ghostwriter/recorded/hypothesis_module_magic.txt
+++ b/hypothesis-python/tests/ghostwriter/recorded/hypothesis_module_magic.txt
@@ -42,8 +42,8 @@ def test_fuzz_find(
     )
 
 
-@given(value=st.text())
-def test_fuzz_note(value: str) -> None:
+@given(value=st.from_type(object))
+def test_fuzz_note(value: object) -> None:
     hypothesis.note(value=value)