Repository: HypothesisWorks/hypothesis

Issue:
False positive HypothesisDeprecationWarning
Just upgraded to Hypothesis 3.8.2, and I'm getting the following warning:

```
.venv/lib/python3.6/site-packages/hypothesis/strategies.py:416: HypothesisDeprecationWarning: Cannot sample from <enum 'AnEnum'>, not a sequence.  Hypothesis goes to some length to ensure that sampling an element from a collection (with `sampled_from` or `choices`) is replayable and can be minimised.  To replay a saved example, the sampled values must have the same iteration order on every run - ruling out sets, dicts, etc due to hash randomisation.  Most cases can simply use `sorted(values)`, but mixed types or special values such as math.nan require careful handling - and note that when simplifying an example, Hypothesis treats earlier values as simpler.
  elements = check_sample(elements)
```
It's a great warning but I think I'm getting a false positive:
```
from hypothesis import given
from hypothesis.strategies import sampled_from

from enum import Enum


class AnEnum(Enum):
    A = "A"
    B = "B"


@given(sampled_from(AnEnum))
def test_enum(e):
    print(e)
```
According to https://docs.python.org/3/library/enum.html, "Enumerations support iteration, in definition order", so this should be fine, right?


Discussion:
👍 We should definitely support enums here. Thanks for the report.
It seems enums are `Iterable`s, but not `Sequence`s. Apparently for something to be a sequence it needs to support "efficient element access using integer indices via the `__getitem__()` special method" and enums don't do that, so yeah technically they aren't sequences.

Also dicts and hashes should be okay for 3.6+ and PyPy?
> Also dicts and hashes should be okay for 3.6+ and PyPy?

Hmm. That's true actually, yeah.

I guess we need a much more refined check for stable ordering.
The ironic thing is that I spent quite a lot of time telling @Zac-HD we shouldn't be clever about this. 😜 

I think being clever about certain concrete collection types might be OK though.
As a side note, we introduced this deprecation in (I think) 3.7.1, which was naughty of us. We shouldn't introduce deprecation warnings in patch releases.

ETA: I say "us" I really mean "me" here. But now we're on one release per change it becomes us in future.
> As a side note, we introduced this deprecation in (I think) 3.7.1, which was naughty of us. We shouldn't introduce deprecation warnings in patch releases.

We definitely shouldn't introduce them without a changelog entry (at least, not one that I can see).
> We definitely shouldn't introduce them without a changelog entry (at least, not one that I can see).

Yeah, mea culpa. I did a very bad job of assembling the changelog entry for 3.7.1. But *that* problem has been fixed by the new release process, while deprecations in patch releases is a thing we've not yet pinned down a fix for (something to add to the review guide).
The main idea was to only support things where the iteration order is stable between runs.  Enums definitely count, but dict iteration order is still implementation-dependent.

I can add that pretty easily, but... @DRMacIver, I already have four patches in the queue waiting for review, and dealing with merge issues in the changelog is pretty tedious when I rebase and then go back to waiting.  I've just added this to the tiny fix in #620, but do you mind reviewing that, #613, and #508 so I can integrate them?

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,6 +22,7 @@
 # on_rtd is whether we are on readthedocs.org
 import os
 import sys
+import datetime
 
 from hypothesis import __version__
 
@@ -51,7 +52,7 @@
 
 # General information about the project.
 project = u'Hypothesis'
-copyright = u'2015, David R. MacIver'
+copyright = u'2013-%s, David R. MacIver' % datetime.datetime.utcnow().year
 author = u'David R. MacIver'
 
 version = __version__
diff --git a/src/hypothesis/internal/conjecture/utils.py b/src/hypothesis/internal/conjecture/utils.py
--- a/src/hypothesis/internal/conjecture/utils.py
+++ b/src/hypothesis/internal/conjecture/utils.py
@@ -17,6 +17,7 @@
 
 from __future__ import division, print_function, absolute_import
 
+import enum
 import math
 from collections import Sequence
 
@@ -104,7 +105,7 @@ def centered_integer_range(data, lower, upper, center):
 
 
 def check_sample(values):
-    if not isinstance(values, Sequence):
+    if not isinstance(values, (Sequence, enum.EnumMeta)):
         note_deprecation(
             ('Cannot sample from %r, not a sequence.  ' % (values,)) +
             'Hypothesis goes to some length to ensure that sampling an '
diff --git a/src/hypothesis/version.py b/src/hypothesis/version.py
--- a/src/hypothesis/version.py
+++ b/src/hypothesis/version.py
@@ -17,5 +17,5 @@
 
 from __future__ import division, print_function, absolute_import
 
-__version_info__ = (3, 8, 3)
+__version_info__ = (3, 8, 4)
 __version__ = '.'.join(map(str, __version_info__))


Test patch:
diff --git a/tests/cover/test_sampled_from.py b/tests/cover/test_sampled_from.py
--- a/tests/cover/test_sampled_from.py
+++ b/tests/cover/test_sampled_from.py
@@ -17,11 +17,15 @@
 
 from __future__ import division, print_function, absolute_import
 
+import enum
+
 from hypothesis import given, settings
 from hypothesis.errors import HypothesisDeprecationWarning
 from tests.common.utils import fails_with
 from hypothesis.strategies import sampled_from
 
+an_enum = enum.Enum('A', 'a b c')
+
 
 @given(sampled_from((1, 2)))
 @settings(min_satisfying_examples=10)
@@ -38,3 +42,9 @@ def test_cannot_sample_sets_in_strict_mode():
 def test_can_sample_sets_while_deprecated():
     with settings(strict=False):
         assert sampled_from(set('abc')).example() in 'abc'
+
+
+@given(sampled_from(an_enum))
+def test_can_sample_enums(member):
+    with settings(strict=True):
+        assert isinstance(member, an_enum)