Repository: HypothesisWorks/hypothesis

Issue:
print_blob setting does not validate its value
The `print_blob` setting controls whether a `@reproduce_failure` blob is printed. It is supposed to be set to a value of `PrintSettings`.

However, because this setting has no validation, it is currently possible to specify other values. With the current code, this is effectively equivalent to specifying `PrintSettings.NEVER`.

In particular:
- If the user specifies `print_blob=False`, it will accidentally have the intended effect, but for the wrong reasons.
- If the user specifies `print_blob=True`, no blob is ever printed, with no clue as to why.


Discussion:

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/_settings.py b/hypothesis-python/src/hypothesis/_settings.py
--- a/hypothesis-python/src/hypothesis/_settings.py
+++ b/hypothesis-python/src/hypothesis/_settings.py
@@ -821,6 +821,29 @@ class PrintSettings(Enum):
     ALWAYS = 2
     """Always print a blob on failure."""
 
+    def __repr__(self):
+        return 'PrintSettings.%s' % (self.name,)
+
+
+def _validate_print_blob(value):
+    if isinstance(value, bool):
+        if value:
+            replacement = PrintSettings.ALWAYS
+        else:
+            replacement = PrintSettings.NEVER
+
+        note_deprecation(
+            'Setting print_blob=%r is deprecated and will become an error '
+            'in a future version of Hypothesis. Use print_blob=%r instead.' % (
+                value, replacement,
+            )
+        )
+        return replacement
+
+    # Values that aren't bool or PrintSettings will be turned into hard errors
+    # by the 'options' check.
+    return value
+
 
 settings._define_setting(
     'print_blob',
@@ -831,7 +854,9 @@ class PrintSettings(Enum):
 
 See :ref:`the documentation on @reproduce_failure <reproduce_failure>` for
 more details of this behaviour.
-"""
+""",
+    validator=_validate_print_blob,
+    options=tuple(PrintSettings),
 )
 
 settings.lock_further_definitions()


Test patch:
diff --git a/hypothesis-python/tests/cover/test_settings.py b/hypothesis-python/tests/cover/test_settings.py
--- a/hypothesis-python/tests/cover/test_settings.py
+++ b/hypothesis-python/tests/cover/test_settings.py
@@ -18,6 +18,7 @@
 from __future__ import division, print_function, absolute_import
 
 import os
+import re
 import sys
 import subprocess
 from tempfile import mkdtemp
@@ -32,8 +33,8 @@
     checks_deprecated_behaviour
 from hypothesis.database import ExampleDatabase, \
     DirectoryBasedExampleDatabase
-from hypothesis._settings import Verbosity, settings, default_variable, \
-    note_deprecation
+from hypothesis._settings import Verbosity, PrintSettings, settings, \
+    default_variable, note_deprecation
 from hypothesis.utils.conventions import not_set
 
 
@@ -401,3 +402,23 @@ def test_setattr_on_settings_singleton_is_error():
     # Should be setting attributes on settings.default, not settings!
     with pytest.raises(AttributeError):
         settings.max_examples = 10
+
+
+@pytest.mark.parametrize(('value', 'replacement', 'suggestion'), [
+    (False, PrintSettings.NEVER, 'PrintSettings.NEVER'),
+    (True, PrintSettings.ALWAYS, 'PrintSettings.ALWAYS'),
+])
+def test_can_set_print_blob_to_deprecated_bool(value, replacement, suggestion):
+    with pytest.warns(
+        HypothesisDeprecationWarning,
+        match=re.escape(suggestion),
+    ):
+        s = settings(print_blob=value)
+
+    assert s.print_blob == replacement
+
+
+@pytest.mark.parametrize('value', [0, 1, 'always'])
+def test_can_not_set_print_blob_to_non_print_settings(value):
+    with pytest.raises(InvalidArgument):
+        settings(print_blob=value)