Repository: HypothesisWorks/hypothesis

Issue:
Question: Is it possible to define uuids() so that it only generates uuid4() ?
Is it possible to define uuids() so that it only generates uuid4() ?

Since I'm testing some other stuff, I'd like to have the generators only to create uuid4s.


Discussion:
It isn't, but the UUID strategy [isn't terribly complicated](https://github.com/HypothesisWorks/hypothesis-python/blob/master/src/hypothesis/strategies.py#L1177), and nor is the standard library code [for generating uuid4s](https://github.com/python/cpython/blob/master/Lib/uuid.py#L631) so it shouldn't be too hard to roll your own. Something like the following should work (I've not tested it though):

```
import hypothesis.strategies as st

@st.composite
def uuid4s(draw):
    rng = draw(st.shared(st.randoms(), key='hypothesis.strategies.uuids.generator'))
    return  UUID(int=rng.getrandbits(128), version=4)
```

A pull request adding UUID version support to hypothesis.strategies would be gratefully accepted if you wanted to make one. :-)
A good first-time contribution: 
- [ ] add a `version=None` keyword argument to `hypothesis.strategies.uuids`, and pass it through to the call to [`uuid.UUID`](https://docs.python.org/3/library/uuid.html#uuid.UUID).
- [ ] Raise `InvalidArgument` with a useful message if the version is not in `(None, 1, 2, 3, 4, 5)`.
- [ ] Write a test that Hypothesis can generate UUIDS of each allowed version.

Resolving patch:
diff --git a/src/hypothesis/strategies.py b/src/hypothesis/strategies.py
--- a/src/hypothesis/strategies.py
+++ b/src/hypothesis/strategies.py
@@ -1511,16 +1511,25 @@ def do_draw(self, data):
 
 @cacheable
 @defines_strategy_with_reusable_values
-def uuids():
+def uuids(version=None):
     """Returns a strategy that generates :class:`UUIDs <uuid.UUID>`.
 
+    If the optional version argument is given, value is passed through
+    to :class:`~python:uuid.UUID` and only UUIDs of that version will
+    be generated.
+
     All returned values from this will be unique, so e.g. if you do
     ``lists(uuids())`` the resulting list will never contain duplicates.
 
     """
     from uuid import UUID
+    if version not in (None, 1, 2, 3, 4, 5):
+        raise InvalidArgument((
+            'version=%r, but version must be in (None, 1, 2, 3, 4, 5) '
+            'to pass to the uuid.UUID constructor.') % (version, )
+        )
     return shared(randoms(), key='hypothesis.strategies.uuids.generator').map(
-        lambda r: UUID(int=r.getrandbits(128))
+        lambda r: UUID(version=version, int=r.getrandbits(128))
     )
 
 


Test patch:
diff --git a/tests/cover/test_direct_strategies.py b/tests/cover/test_direct_strategies.py
--- a/tests/cover/test_direct_strategies.py
+++ b/tests/cover/test_direct_strategies.py
@@ -139,6 +139,7 @@ def fn_ktest(*fnkwargs):
     (ds.times, {
         'min_value': time(2, 0),
         'max_value': time(1, 0)}),
+    (ds.uuids, {'version': 6}),
 )
 def test_validates_keyword_arguments(fn, kwargs):
     with pytest.raises(InvalidArgument):
diff --git a/tests/cover/test_uuids.py b/tests/cover/test_uuids.py
--- a/tests/cover/test_uuids.py
+++ b/tests/cover/test_uuids.py
@@ -17,6 +17,8 @@
 
 from __future__ import division, print_function, absolute_import
 
+import pytest
+
 import hypothesis.strategies as st
 from hypothesis import find, given
 
@@ -30,3 +32,12 @@ def test_are_unique(ls):
 def test_retains_uniqueness_in_simplify(ls, rnd):
     ts = find(st.lists(st.uuids()), lambda x: len(x) >= 5, random=rnd)
     assert len(ts) == len(set(ts)) == 5
+
+
+@pytest.mark.parametrize('version', (1, 2, 3, 4, 5))
+def test_can_generate_specified_version(version):
+    @given(st.uuids(version=version))
+    def inner(uuid):
+        assert version == uuid.version
+
+    inner()