[Python-modules-commits] [python-hypothesis] 01/03: Import python-hypothesis_3.6.1.orig.tar.gz

Tristan Seligmann mithrandi at moszumanska.debian.org
Mon Dec 26 14:09:25 UTC 2016


This is an automated email from the git hooks/post-receive script.

mithrandi pushed a commit to branch master
in repository python-hypothesis.

commit 56b9a3bc36584695ed36682c464b94debb064d17
Author: Tristan Seligmann <mithrandi at debian.org>
Date:   Mon Dec 26 14:45:34 2016 +0200

    Import python-hypothesis_3.6.1.orig.tar.gz
---
 CONTRIBUTING.rst                                   |   1 +
 README.rst                                         |  10 ++
 docs/changes.rst                                   |  25 ++++
 docs/extras.rst                                    |   5 +
 docs/index.rst                                     |   1 +
 docs/settings.rst                                  |   2 +-
 docs/stateful.rst                                  |   2 +-
 docs/strategies.rst                                |  10 ++
 scripts/basic-test.sh                              |   5 +-
 setup.py                                           |   5 +-
 src/hypothesis/_settings.py                        |   2 +
 src/hypothesis/extra/django/models.py              |  31 ++++-
 src/hypothesis/internal/charmap.py                 |   1 +
 src/hypothesis/internal/conjecture/data.py         |  22 +++-
 src/hypothesis/internal/conjecture/engine.py       | 104 ++++++++++++----
 src/hypothesis/internal/conjecture/minimizer.py    |  55 +++++++--
 src/hypothesis/internal/debug.py                   |   1 +
 src/hypothesis/searchstrategy/flatmapped.py        |   8 ++
 src/hypothesis/searchstrategy/strategies.py        |  26 ++++
 src/hypothesis/stateful.py                         |   2 +
 src/hypothesis/strategies.py                       |  24 +++-
 src/hypothesis/tools/mergedbs.py                   |   1 +
 src/hypothesis/vendor/pretty.py                    |   3 +
 src/hypothesis/version.py                          |   2 +-
 tests/common/__init__.py                           |   1 +
 tests/common/utils.py                              |   1 +
 tests/cover/test_composite.py                      |  33 ++++++
 tests/cover/test_core.py                           |   1 +
 tests/cover/test_database_agreement.py             |   1 +
 tests/cover/test_flatmap.py                        |  10 ++
 tests/cover/test_pretty.py                         |   2 +
 tests/cover/test_runner_strategy.py                |   1 +
 tests/cover/test_stateful.py                       |   4 +
 .../django/toystore/test_email_strategies.py       |  17 ++-
 tests/nocover/test_compat.py                       |   3 +
 tests/nocover/test_descriptortests.py              |   2 +
 tests/nocover/test_pretty_repr.py                  |   4 -
 tests/nocover/test_statistical_distribution.py     | 132 ++++++++++++++++++++-
 tests/nocover/test_strategy_state.py               |   1 +
 tests/py3/test_asyncio.py                          |   1 +
 tox.ini                                            |   2 -
 41 files changed, 502 insertions(+), 62 deletions(-)

diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index d638a5c..18341e4 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -153,6 +153,7 @@ their individual contributions.
 * `Marius Gedminas <https://www.github.com/mgedmin>`_ (`marius at gedmin.as <mailto:marius at gedmin.as>`_)
 * `Markus Unterwaditzer <http://github.com/untitaker/>`_ (`markus at unterwaditzer.net <mailto:markus at unterwaditzer.net>`_)
 * `Matt Bachmann <https://www.github.com/bachmann1234>`_ (`bachmann.matt at gmail.com <mailto:bachmann.matt at gmail.com>`_)
+* `mulkieran <https://www.github.com/mulkieran>`_
 * `Nicholas Chammas <https://www.github.com/nchammas>`_
 * `Richard Boulton <https://www.github.com/rboulton>`_ (`richard at tartarus.org <mailto:richard at tartarus.org>`_)
 * `Saul Shanabrook <https://www.github.com/saulshanabrook>`_ (`s.shanabrook at gmail.com <mailto:s.shanabrook at gmail.com>`_)
diff --git a/README.rst b/README.rst
index 8da33c4..3f71cb2 100644
--- a/README.rst
+++ b/README.rst
@@ -26,6 +26,16 @@ Hypothesis is extremely practical and advances the state of the art of
 unit testing by some way. It's easy to use, stable, and powerful. If
 you're not using Hypothesis to test your project then you're missing out.
 
+------------------------
+Quick Start/Installation
+------------------------
+If you just want to get started:
+
+.. code-block::
+
+  pip install hypothesis
+
+
 -----------------
 Links of interest
 -----------------
diff --git a/docs/changes.rst b/docs/changes.rst
index c0e311d..34a960f 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -21,6 +21,31 @@ Hypothesis APIs come in three flavours:
 You should generally assume that an API is internal unless you have specific
 information to the contrary.
 
+
+------------------
+3.6.1 - 2016-12-20
+------------------
+
+This release fixes a dependency problem and makes some small behind the scenes
+improvements.
+
+* The fake-factory dependency was renamed to faker. If you were depending on
+  it through hypothesis[django] or hypothesis[fake-factory] without pinning it
+  yourself then it would have failed to install properly. This release changes
+  it so that hypothesis[fakefactory] (which can now also be installed as
+  hypothesis[faker]) will install the renamed faker package instead.
+* This release also removed the dependency of hypothesis[django] on
+  hypothesis[fakefactory] - it was only being used for emails. These now use
+  a custom strategy that isn't from fakefactory. As a result you should also
+  see performance improvements of tests which generated User objects or other
+  things with email fields, as well as better shrinking of email addresses.
+* The distribution of code using nested calls to one_of or the | operator for
+  combining strategies has been improved, as branches are now flattened to give
+  a more uniform distribution.
+* Examples using composite or flatmap should now shrink better. In particular
+  this will affect things which work by first generating a length and then
+  generating that many items, which have historically not shrunk very well.
+
 ------------------
 3.6.0 - 2016-10-31
 ------------------
diff --git a/docs/extras.rst b/docs/extras.rst
index a58806a..62778a2 100644
--- a/docs/extras.rst
+++ b/docs/extras.rst
@@ -236,3 +236,8 @@ It lives in the ``hypothesis.extra.numpy`` package.
       array([-2], dtype=int8)
       >>> rla.example()
       array([ 7, -6, -2], dtype=int8)
+
+**Note**: To generate large arrays/matrices, or even medium-sized matrices that
+have 3 or more dimensions, it's necessary to increase the ``buffer_size``
+setting to be greater than its default of ``8192`` (see the 
+:doc:`Settings documentation <settings>` for details on how to do this).
diff --git a/docs/index.rst b/docs/index.rst
index 7d08ab9..aee3dd5 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -71,6 +71,7 @@ check out some of the
   manifesto
   endorsements
   usage
+  strategies
   changes
   development
   support
diff --git a/docs/settings.rst b/docs/settings.rst
index 4323f27..5615b16 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -43,7 +43,7 @@ Available settings
 .. autoclass:: settings
     :members: max_examples, max_iterations, min_satisfying_examples,
         max_shrinks, timeout, strict, database_file, stateful_step_count, 
-        database, perform_health_check, suppress_health_check
+        database, perform_health_check, suppress_health_check, buffer_size
 
 .. _verbose-output:
 
diff --git a/docs/stateful.rst b/docs/stateful.rst
index 37f3e54..68841b2 100644
--- a/docs/stateful.rst
+++ b/docs/stateful.rst
@@ -202,7 +202,7 @@ We've now written a really noddy tree balancing implementation.  This takes
 trees and puts them into a new bundle of data, and we only assert that things
 in the balanced_trees bundle are actually balanced.
 
-If you run this it will sit their silently for a while (you can turn on
+If you run this it will sit there silently for a while (you can turn on
 :ref:`verbose output <verbose-output>` to get slightly more information about
 what's happening. debug will give you all the intermediate programs being run)
 and then run, telling you your test has passed! Our balancing algorithm worked.
diff --git a/docs/strategies.rst b/docs/strategies.rst
new file mode 100644
index 0000000..40f70f3
--- /dev/null
+++ b/docs/strategies.rst
@@ -0,0 +1,10 @@
+=======================================================
+Open Source Projects contributing Hypothesis Strategies
+=======================================================
+
+The following is a non-exhaustive list of open source projects that make
+Hypothesis strategies available. If you're aware of any others please add them
+the list!  The only inclusion criterion right now is that if it's a Python
+library then it should be available on pypi.
+
+* `hs-dbus-signature <https://github.com/stratis-storage/hs-dbus-signature>`_
diff --git a/scripts/basic-test.sh b/scripts/basic-test.sh
index 5cda689..550ad92 100755
--- a/scripts/basic-test.sh
+++ b/scripts/basic-test.sh
@@ -46,15 +46,16 @@ if [ "$DARWIN" = true ]; then
 fi
 
 # fake-factory doesn't have a correct universal wheel
-pip install --no-use-wheel .[fakefactory]
+pip install --no-use-wheel faker
 $PYTEST tests/fakefactory/
+pip uninstall -y faker
 
 if [ "$(python -c 'import platform; print(platform.python_implementation())')" != "PyPy" ]; then
   if [ "$(python -c 'import sys; print(sys.version_info[:2] <= (2, 6))')" != "True" ] ; then
   if [ "$(python -c 'import sys; print(sys.version_info[0] == 2 or sys.version_info[:2] >= (3, 4))')" == "True" ] ; then
     pip install .[django]
     python -m tests.django.manage test tests.django
-    pip uninstall -y django fake-factory
+    pip uninstall -y django
   fi
   fi
 
diff --git a/setup.py b/setup.py
index 279627d..d147793 100644
--- a/setup.py
+++ b/setup.py
@@ -39,14 +39,15 @@ assert __version__ is not None
 
 extras = {
     'datetime':  ["pytz"],
-    'fakefactory': ["fake-factory>=0.6.0"],
+    'fakefactory': ["Faker>=0.7.0,<=0.7.1"],
     'django': ['pytz', 'django>=1.7'],
     'numpy': ['numpy>=1.9.0'],
     'pytest': ['pytest>=2.7.0'],
 }
 
+extras['faker'] = extras['fakefactory']
+
 extras['all'] = sorted(sum(extras.values(), []))
-extras['django'].extend(extras['fakefactory'])
 
 extras[":python_version == '2.7'"] = ['enum34']
 extras[":python_version == '3.3'"] = ['enum34']
diff --git a/src/hypothesis/_settings.py b/src/hypothesis/_settings.py
index fc4a2cc..3b1b403 100644
--- a/src/hypothesis/_settings.py
+++ b/src/hypothesis/_settings.py
@@ -74,6 +74,7 @@ class settingsProperty(object):
             'default value: %r' % (getattr(settings.default, self.name),)
         ))
 
+
 default_variable = DynamicVariable(None)
 
 
@@ -494,6 +495,7 @@ class Verbosity(object):
             return result
         raise InvalidArgument('No such verbosity level %r' % (key,))
 
+
 Verbosity.quiet = Verbosity('quiet', 0)
 Verbosity.normal = Verbosity('normal', 1)
 Verbosity.verbose = Verbosity('verbose', 2)
diff --git a/src/hypothesis/extra/django/models.py b/src/hypothesis/extra/django/models.py
index dfe79af..63a5cda 100644
--- a/src/hypothesis/extra/django/models.py
+++ b/src/hypothesis/extra/django/models.py
@@ -17,6 +17,7 @@
 
 from __future__ import division, print_function, absolute_import
 
+import string
 from decimal import Decimal
 
 import django.db.models as dm
@@ -24,7 +25,6 @@ from django.db import IntegrityError
 from django.core.exceptions import ValidationError
 
 import hypothesis.strategies as st
-import hypothesis.extra.fakefactory as ff
 from hypothesis.errors import InvalidArgument
 from hypothesis.extra.datetime import datetimes
 from hypothesis.utils.conventions import UniqueIdentifier
@@ -95,6 +95,33 @@ def validator_to_filter(f):
     return validate
 
 
+safe_letters = string.ascii_letters + string.digits + '_-'
+
+domains = st.builds(
+    lambda x, y: '.'.join(x + [y]),
+    st.lists(st.text(safe_letters, min_size=1), min_size=1), st.sampled_from([
+        'com', 'net', 'org', 'biz', 'info',
+    ])
+)
+
+
+email_domains = st.one_of(
+    domains,
+    st.sampled_from(['gmail.com', 'yahoo.com', 'hotmail.com'])
+)
+
+base_emails = st.text(safe_letters, min_size=1)
+
+emails_with_plus = st.builds(
+    lambda x, y: '%s+%s' % (x, y), base_emails, base_emails
+)
+
+emails = st.builds(
+    lambda x, y: '%s@%s' % (x, y),
+    st.one_of(base_emails, emails_with_plus), email_domains
+)
+
+
 def _get_strategy_for_field(f):
     if isinstance(f, dm.AutoField):
         return default_value
@@ -104,7 +131,7 @@ def _get_strategy_for_field(f):
             choices.append(u'')
         strategy = st.sampled_from(choices)
     elif isinstance(f, dm.EmailField):
-        return ff.fake_factory(u'email')
+        return emails
     elif type(f) in (dm.TextField, dm.CharField):
         strategy = st.text(min_size=(None if f.blank else 1),
                            max_size=f.max_length)
diff --git a/src/hypothesis/internal/charmap.py b/src/hypothesis/internal/charmap.py
index 5f72ace..43147fe 100644
--- a/src/hypothesis/internal/charmap.py
+++ b/src/hypothesis/internal/charmap.py
@@ -33,6 +33,7 @@ def charmap_file():
         'charmap.pickle.gz'
     )
 
+
 _charmap = None
 
 
diff --git a/src/hypothesis/internal/conjecture/data.py b/src/hypothesis/internal/conjecture/data.py
index b00ff48..f0e22f3 100644
--- a/src/hypothesis/internal/conjecture/data.py
+++ b/src/hypothesis/internal/conjecture/data.py
@@ -20,8 +20,9 @@ from __future__ import division, print_function, absolute_import
 from enum import IntEnum
 
 from hypothesis.errors import Frozen, InvalidArgument
-from hypothesis.internal.compat import hbytes, text_type, int_to_bytes, \
-    benchmark_time, unicode_safe_repr, reasonable_byte_type
+from hypothesis.internal.compat import hbytes, hrange, text_type, \
+    int_to_bytes, benchmark_time, unicode_safe_repr, \
+    reasonable_byte_type
 
 
 def uniform(random, n):
@@ -41,6 +42,7 @@ class StopTest(BaseException):
         super(StopTest, self).__init__(repr(testcounter))
         self.testcounter = testcounter
 
+
 global_test_counter = 0
 
 
@@ -74,6 +76,7 @@ class TestData(object):
         global_test_counter += 1
         self.start_time = benchmark_time()
         self.events = set()
+        self.bind_points = set()
 
     def __assert_not_frozen(self, name):
         if self.frozen:
@@ -104,6 +107,19 @@ class TestData(object):
             if not self.frozen:
                 self.stop_example()
 
+    def mark_bind(self):
+        """Marks a point as somewhere that a bind occurs - that is, data
+        drawn after this point may depend significantly on data drawn prior
+        to this point.
+
+        Having points like this explicitly marked allows for better shrinking,
+        as we run a pass which tries to shrink the byte stream prior to a bind
+        point while rearranging what comes after somewhat to allow for more
+        flexibility. Trying that at every point in the data stream is
+        prohibitively expensive, but trying it at a couple dozen is basically
+        fine."""
+        self.bind_points.add(self.index)
+
     def start_example(self):
         self.__assert_not_frozen('start_example')
         self.interval_stack.append(self.index)
@@ -132,7 +148,7 @@ class TestData(object):
         self.finish_time = benchmark_time()
         # Intervals are sorted as longest first, then by interval start.
         for l in self.intervals_by_level:
-            for i in range(len(l) - 1):
+            for i in hrange(len(l) - 1):
                 if l[i][1] == l[i + 1][0]:
                     self.intervals.append((l[i][0], l[i + 1][1]))
         self.intervals = sorted(
diff --git a/src/hypothesis/internal/conjecture/engine.py b/src/hypothesis/internal/conjecture/engine.py
index f188941..06982f5 100644
--- a/src/hypothesis/internal/conjecture/engine.py
+++ b/src/hypothesis/internal/conjecture/engine.py
@@ -376,6 +376,8 @@ class TestRunner(object):
 
         while self.changed > change_counter:
             change_counter = self.changed
+
+            self.debug('Random interval deletes')
             failed_deletes = 0
             while self.last_data.intervals and failed_deletes < 10:
                 if self.random.randint(0, 1):
@@ -394,6 +396,8 @@ class TestRunner(object):
                     failed_deletes = 0
                 else:
                     failed_deletes += 1
+
+            self.debug('Structured interval deletes')
             i = 0
             while i < len(self.last_data.intervals):
                 u, v = self.last_data.intervals[i]
@@ -402,13 +406,22 @@ class TestRunner(object):
                     self.last_data.buffer[v:]
                 ):
                     i += 1
-            i = 0
-            while i + 1 < len(self.last_data.buffer):
-                if not self.incorporate_new_buffer(
-                    self.last_data.buffer[:i] +
-                    self.last_data.buffer[i + 1:]
-                ):
-                    i += 1
+
+            if change_counter != self.changed:
+                self.debug('Restarting')
+                continue
+
+            self.debug('Lexicographical minimization of whole buffer')
+            minimize(
+                self.last_data.buffer, self.incorporate_new_buffer,
+                cautious=True
+            )
+
+            if change_counter != self.changed:
+                self.debug('Restarting')
+                continue
+
+            self.debug('Replacing blocks with simpler blocks')
             i = 0
             while i < len(self.last_data.blocks):
                 u, v = self.last_data.blocks[i]
@@ -427,6 +440,7 @@ class TestRunner(object):
                         break
                 i += 1
 
+            self.debug('Simultaneous shrinking of duplicated blocks')
             block_counter = -1
             while block_counter < self.changed:
                 block_counter = self.changed
@@ -453,6 +467,7 @@ class TestRunner(object):
                         self.random
                     )
 
+            self.debug('Shrinking of individual blocks')
             i = 0
             while i < len(self.last_data.blocks):
                 u, v = self.last_data.blocks[i]
@@ -465,24 +480,63 @@ class TestRunner(object):
                 )
                 i += 1
 
-            i = 0
-            alternatives = None
-            while i < len(self.last_data.intervals):
-                if alternatives is None:
-                    alternatives = sorted(set(
-                        self.last_data.buffer[u:v]
-                        for u, v in self.last_data.intervals), key=len)
-                u, v = self.last_data.intervals[i]
-                for a in alternatives:
-                    buf = self.last_data.buffer
-                    if (
-                        len(a) < v - u or
-                        (len(a) == (v - u) and a < buf[u:v])
-                    ):
-                        if self.incorporate_new_buffer(buf[:u] + a + buf[v:]):
-                            alternatives = None
-                            break
-                i += 1
+            self.debug('Replacing intervals with simpler intervals')
+
+            interval_counter = -1
+            while interval_counter != self.changed:
+                interval_counter = self.changed
+                i = 0
+                alternatives = None
+                while i < len(self.last_data.intervals):
+                    if alternatives is None:
+                        alternatives = sorted(set(
+                            self.last_data.buffer[u:v]
+                            for u, v in self.last_data.intervals), key=len)
+                    u, v = self.last_data.intervals[i]
+                    for a in alternatives:
+                        buf = self.last_data.buffer
+                        if (
+                            len(a) < v - u or
+                            (len(a) == (v - u) and a < buf[u:v])
+                        ):
+                            if self.incorporate_new_buffer(
+                                buf[:u] + a + buf[v:]
+                            ):
+                                alternatives = None
+                                break
+                    i += 1
+
+            if change_counter != self.changed:
+                self.debug('Restarting')
+                continue
+
+            self.debug('Shuffling suffixes while shrinking %r' % (
+                self.last_data.bind_points,
+            ))
+            b = 0
+            while b < len(self.last_data.bind_points):
+                cutoff = sorted(self.last_data.bind_points)[b]
+
+                def test_value(prefix):
+                    for t in hrange(5):
+                        alphabet = {}
+                        for i, j in self.last_data.blocks[b:]:
+                            alphabet.setdefault(j - i, []).append((i, j))
+                        if t > 0:
+                            for v in alphabet.values():
+                                self.random.shuffle(v)
+                        buf = bytearray(prefix)
+                        for i, j in self.last_data.blocks[b:]:
+                            u, v = alphabet[j - i].pop()
+                            buf.extend(self.last_data.buffer[u:v])
+                        if self.incorporate_new_buffer(hbytes(buf)):
+                            return True
+                    return False
+                minimize(
+                    self.last_data.buffer[:cutoff], test_value, cautious=True
+                )
+                b += 1
+
         self.exit_reason = ExitReason.finished
 
     def event_to_string(self, event):
diff --git a/src/hypothesis/internal/conjecture/minimizer.py b/src/hypothesis/internal/conjecture/minimizer.py
index f795be9..111f70b 100644
--- a/src/hypothesis/internal/conjecture/minimizer.py
+++ b/src/hypothesis/internal/conjecture/minimizer.py
@@ -40,18 +40,19 @@ to do better in practice:
 
 class Minimizer(object):
 
-    def __init__(self, initial, condition, random):
+    def __init__(self, initial, condition, random, cautious):
         self.current = hbytes(initial)
         self.size = len(self.current)
         self.condition = condition
         self.random = random
+        self.cautious = cautious
         self.changes = 0
 
     def incorporate(self, buffer):
         assert isinstance(buffer, hbytes)
         assert len(buffer) == self.size
         assert buffer <= self.current
-        if self.condition(buffer):
+        if buffer != self.current and self.condition(buffer):
             self.current = buffer
             self.changes += 1
             return True
@@ -82,15 +83,38 @@ class Minimizer(object):
             return
         if self.incorporate(hbytes(self.size)):
             return
-        for c in hrange(max(self.current)):
-            if self.incorporate(
-                hbytes(min(b, c) for b in self.current)
-            ):
-                break
-
         change_counter = -1
         while self.current and change_counter < self.changes:
             change_counter = self.changes
+            for c in hrange(max(self.current)):
+                if self.incorporate(
+                    hbytes(min(b, c) for b in self.current)
+                ):
+                    break
+
+            for c in sorted(set(self.current), reverse=True):
+                for d in hrange(c):
+                    if self.incorporate(
+                        hbytes(d if b == c else b for b in self.current)
+                    ):
+                        break
+
+            for c in hrange(max(self.current)):
+                k = len(self.current) // 2
+                while k > 0:
+                    i = 0
+                    while i + k <= len(self.current):
+                        self.incorporate(
+                            self.current[:i] +
+                            hbytes(min(b, c) for b in self.current[i:i + k]) +
+                            self.current[i + k:]
+                        )
+                        i += k
+                    k //= 2
+
+            if change_counter != self.changes or self.cautious:
+                continue
+
             for i in hrange(self.size):
                 t = self.current[i]
                 if t > 0:
@@ -126,7 +150,18 @@ for b in hrange(10, 256):
     small_shrinks.append(sorted(result))
 
 
-def minimize(initial, condition, random=None):
-    m = Minimizer(initial, condition, random)
+def minimize(initial, condition, random=None, cautious=False):
+    """Perform a lexicographical minimization of the byte string 'initial' such
+    that the predicate 'condition' returns True, and return the minimized
+    string.
+
+    If 'cautious' is set to True, this will only consider strings that
+    satisfy the stronger condition that no individual byte is increased
+    from the original. e.g. normally bytes([0, 255]) is considered a
+    valid shrink of bytes([1, 0]), but if cautious is set it will not
+    be.
+
+    """
+    m = Minimizer(initial, condition, random, cautious)
     m.run()
     return m.current
diff --git a/src/hypothesis/internal/debug.py b/src/hypothesis/internal/debug.py
index bbf14f3..a36d662 100644
--- a/src/hypothesis/internal/debug.py
+++ b/src/hypothesis/internal/debug.py
@@ -33,6 +33,7 @@ class Timeout(BaseException):
 class CatchableTimeout(Exception):
     pass
 
+
 try:
     signal.SIGALRM
     # The tests here have a tendency to run away with themselves a it if
diff --git a/src/hypothesis/searchstrategy/flatmapped.py b/src/hypothesis/searchstrategy/flatmapped.py
index 756e5e4..89ecc35 100644
--- a/src/hypothesis/searchstrategy/flatmapped.py
+++ b/src/hypothesis/searchstrategy/flatmapped.py
@@ -40,4 +40,12 @@ class FlatMapStrategy(SearchStrategy):
 
     def do_draw(self, data):
         source = data.draw(self.flatmapped_strategy)
+        data.mark_bind()
         return data.draw(self.expand(source))
+
+    @property
+    def branches(self):
+        return [
+            FlatMapStrategy(strategy=strategy, expand=self.expand)
+            for strategy in self.flatmapped_strategy.branches
+        ]
diff --git a/src/hypothesis/searchstrategy/strategies.py b/src/hypothesis/searchstrategy/strategies.py
index 8a9b33b..c25e03c 100644
--- a/src/hypothesis/searchstrategy/strategies.py
+++ b/src/hypothesis/searchstrategy/strategies.py
@@ -157,6 +157,10 @@ class SearchStrategy(object):
             strategy=self,
         )
 
+    @property
+    def branches(self):
+        return [self]
+
     def __or__(self, other):
         """Return a strategy which produces values by randomly drawing from one
         of this strategy or the other strategy.
@@ -227,6 +231,13 @@ class OneOfStrategy(SearchStrategy):
         for e in self.element_strategies:
             e.validate()
 
+    @property
+    def branches(self):
+        if self.bias is None:
+            return self.element_strategies
+        else:
+            return [self]
+
 
 class MappedSearchStrategy(SearchStrategy):
 
@@ -271,6 +282,13 @@ class MappedSearchStrategy(SearchStrategy):
                     raise
         reject()
 
+    @property
+    def branches(self):
+        return [
+            MappedSearchStrategy(pack=self.pack, strategy=strategy)
+            for strategy in self.mapped_strategy.branches
+        ]
+
 
 class FilteredStrategy(SearchStrategy):
 
@@ -309,3 +327,11 @@ class FilteredStrategy(SearchStrategy):
             self,
         ))
         data.mark_invalid()
+
+    @property
+    def branches(self):
+        branches = [
+            FilteredStrategy(strategy=strategy, condition=self.condition)
+            for strategy in self.filtered_strategy.branches
+        ]
+        return branches
diff --git a/src/hypothesis/stateful.py b/src/hypothesis/stateful.py
index 4b8accd..1da91af 100644
--- a/src/hypothesis/stateful.py
+++ b/src/hypothesis/stateful.py
@@ -196,6 +196,7 @@ class GenericStateMachine(object):
         )
         return StateMachineTestCase
 
+
 GenericStateMachine.find_breaking_runner = classmethod(find_breaking_runner)
 
 
@@ -271,6 +272,7 @@ class Bundle(SearchStrategy):
         bundle.insert(integer_range(data, 0, len(bundle)), reference)
         return machine.names_to_values[reference.name]
 
+
 RULE_MARKER = u'hypothesis_stateful_rule'
 PRECONDITION_MARKER = u'hypothesis_stateful_precondition'
 
diff --git a/src/hypothesis/strategies.py b/src/hypothesis/strategies.py
index 1fc99b0..c4bd492 100644
--- a/src/hypothesis/strategies.py
+++ b/src/hypothesis/strategies.py
@@ -128,6 +128,7 @@ class Nothing(SearchStrategy):
     def flatmap(self, f):
         return self
 
+
 NOTHING = Nothing()
 
 
@@ -173,16 +174,18 @@ def one_of(*args):
         except TypeError:
             pass
 
+    strategies = []
     for arg in args:
         check_strategy(arg)
-    args = [a for a in args if not a.is_empty]
+        if not arg.is_empty:
+            strategies.extend([s for s in arg.branches if not s.is_empty])
 
-    if not args:
+    if not strategies:
         return nothing()
-    if len(args) == 1:
-        return args[0]
+    if len(strategies) == 1:
+        return strategies[0]
     from hypothesis.searchstrategy.strategies import OneOfStrategy
-    return OneOfStrategy(args)
+    return OneOfStrategy(strategies)
 
 
 @cacheable
@@ -900,7 +903,15 @@ def composite(f):
         class CompositeStrategy(SearchStrategy):
 
             def do_draw(self, data):
-                return f(data.draw, *args, **kwargs)
+                first_draw = [True]
+
+                def draw(strategy):
+                    if not first_draw[0]:
+                        data.mark_bind()
+                    first_draw[0] = False
+                    return data.draw(strategy)
+
+                return f(draw, *args, **kwargs)
         return CompositeStrategy()
     return accept
 
@@ -1031,6 +1042,7 @@ def data():
             return 'data(...)'
 
         def draw(self, strategy):
+            self.data.mark_bind()
             result = self.data.draw(strategy)
             self.count += 1
             note('Draw %d: %r' % (self.count, result))
diff --git a/src/hypothesis/tools/mergedbs.py b/src/hypothesis/tools/mergedbs.py
index 315abbe..ecc3fc5 100644
--- a/src/hypothesis/tools/mergedbs.py
+++ b/src/hypothesis/tools/mergedbs.py
@@ -131,5 +131,6 @@ def main():
     print(u'%d new entries and %d deletions from merge' % (
         result.inserts, result.deletions))
 
+
 if __name__ == u'__main__':
     main()
diff --git a/src/hypothesis/vendor/pretty.py b/src/hypothesis/vendor/pretty.py
index b260a0b..7ba0236 100644
--- a/src/hypothesis/vendor/pretty.py
+++ b/src/hypothesis/vendor/pretty.py
@@ -108,6 +108,7 @@ def _safe_getattr(obj, attr, default=None):
     except Exception:
         return default
 
+
 if PY3:
     CUnicodeIO = StringIO
 else:  # pragma: no cover
@@ -495,6 +496,7 @@ class GroupQueue(object):
         except ValueError:
             pass
 
+
 try:
     _baseclass_reprs = (object.__repr__, types.InstanceType.__repr__)
 except AttributeError:  # Python 3
@@ -859,6 +861,7 @@ def _counter_pprint(obj, p, cycle):
         elif len(obj):
             p.pretty(dict(obj))
 
+
 for_type_by_name('collections', 'defaultdict', _defaultdict_pprint)
 for_type_by_name('collections', 'OrderedDict', _ordereddict_pprint)
 for_type_by_name('ordereddict', 'OrderedDict', _ordereddict_pprint)
diff --git a/src/hypothesis/version.py b/src/hypothesis/version.py
index 5f75f8c..50f1c2c 100644
--- 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, 6, 0)
+__version_info__ = (3, 6, 1)
 __version__ = '.'.join(map(str, __version_info__))
diff --git a/tests/common/__init__.py b/tests/common/__init__.py
index c84e6d1..361bca3 100644
--- a/tests/common/__init__.py
+++ b/tests/common/__init__.py
@@ -54,6 +54,7 @@ ABC = namedtuple('ABC', ('a', 'b', 'c'))
 def abc(x, y, z):
     return builds(ABC, x, y, z)
 
+
 with settings(strict=False):
     standard_types = [
         lists(max_size=0), tuples(), sets(max_size=0), frozensets(max_size=0),
diff --git a/tests/common/utils.py b/tests/common/utils.py
index 8adf3a5..532b2f9 100644
--- a/tests/common/utils.py
+++ b/tests/common/utils.py
@@ -64,4 +64,5 @@ def fails_with(e):
         return inverted_test
     return accepts
 
+
 fails = fails_with(AssertionError)
diff --git a/tests/cover/test_composite.py b/tests/cover/test_composite.py
index 1b82b95..55675d6 100644
--- a/tests/cover/test_composite.py
+++ b/tests/cover/test_composite.py
@@ -18,6 +18,7 @@
 from __future__ import division, print_function, absolute_import
 
 import pytest
+from flaky import flaky
 
 import hypothesis.strategies as st
 from hypothesis import find, given, assume
@@ -95,3 +96,35 @@ def test_composite_of_lists():
         return draw(st.integers()) + draw(st.integers())
 
     assert find(st.lists(f()), lambda x: len(x) >= 10) == [0] * 10
+
+
+ at flaky(min_passes=5, max_runs=5)
+def test_can_shrink_matrices_with_length_param():
+    @st.composite
+    def matrix(draw):
+        rows = draw(st.integers(1, 10))
+        columns = draw(st.integers(1, 10))
+        return [
+            [draw(st.integers(0, 10000)) for _ in range(columns)]
+            for _ in range(rows)
+        ]
+
+    def transpose(m):
+        rows = len(m)
+        columns = len(m[0])
+        result = [
+            [None] * rows
+            for _ in range(columns)
+        ]
+        for i in range(rows):
+            for j in range(columns):
+                result[j][i] = m[i][j]
+        return result
+
+    def is_square(m):
+        return len(m) == len(m[0])
+
+    value = find(matrix(), lambda m: is_square(m) and transpose(m) != m)
+    assert len(value) == 2
+    assert len(value[0]) == 2
+    assert sorted(value[0] + value[1]) == [0, 0, 0, 1]
diff --git a/tests/cover/test_core.py b/tests/cover/test_core.py
index 3555a8c..d6c2d81 100644
--- a/tests/cover/test_core.py
+++ b/tests/cover/test_core.py
@@ -78,6 +78,7 @@ def test_can_time_out_in_simplify():
     run_time = finish - start
     assert run_time <= 0.3
 
+
 some_normal_settings = settings()
 
 
diff --git a/tests/cover/test_database_agreement.py b/tests/cover/test_database_agreement.py
index eb25dc5..a6f0601 100644
--- a/tests/cover/test_database_agreement.py
+++ b/tests/cover/test_database_agreement.py
@@ -75,4 +75,5 @@ class DatabaseComparison(RuleBasedStateMachine):
             d.close()
         shutil.rmtree(self.tempd)
 
+
 TestDBs = DatabaseComparison.TestCase
diff --git a/tests/cover/test_flatmap.py b/tests/cover/test_flatmap.py
index 492b14a..3c32485 100644
--- a/tests/cover/test_flatmap.py
+++ b/tests/cover/test_flatmap.py
@@ -94,3 +94,13 @@ def test_mixed_list_flatmap():
     result = find(s, criterion)
     assert len(result) == 6
     assert set(result) == set([False, u''])
+
+
+ at pytest.mark.parametrize('n', range(1, 10))
+def test_can_shrink_through_a_binding(n):
+    bool_lists = integers(0, 100).flatmap(
+        lambda k: lists(booleans(), min_size=k, max_size=k))
+
+    assert find(
+        bool_lists, lambda x: len(list(filter(bool, x))) >= n
+    ) == [True] * n
diff --git a/tests/cover/test_pretty.py b/tests/cover/test_pretty.py
index 9af4a98..4d2c650 100644
--- a/tests/cover/test_pretty.py
+++ b/tests/cover/test_pretty.py
@@ -102,6 +102,7 @@ def skip_without(mod):
     except ImportError:
         return pytest.mark.skipif(True, reason='Missing %s' % (mod,))
 
+
 assert_raises = pytest.raises
 
 
@@ -149,6 +150,7 @@ class Dummy2(Dummy1):
 class NoModule(object):
     pass
 
+
 NoModule.__module__ = None
 
 
diff --git a/tests/cover/test_runner_strategy.py b/tests/cover/test_runner_strategy.py
index f4a0579..52cb3e0 100644
--- a/tests/cover/test_runner_strategy.py
+++ b/tests/cover/test_runner_strategy.py
@@ -69,4 +69,5 @@ class RunnerStateMachine(GenericStateMachine):
     def execute_step(self, step):
         assert self is step
 
+
 TestState = RunnerStateMachine.TestCase
diff --git a/tests/cover/test_stateful.py b/tests/cover/test_stateful.py
index fa582bd..9de8b68 100644
--- a/tests/cover/test_stateful.py
+++ b/tests/cover/test_stateful.py
@@ -212,6 +212,7 @@ class NotTheLastMachine(RuleBasedStateMachine):
         assert v == self.last
         self.bye_called = True
 
+
 bad_machines = (
     OrderedStateMachine, SetStateMachine, BalancedTrees,
     DepthMachine, RoseTreeStateMachine, NotTheLastMachine,
@@ -354,6 +355,7 @@ class DynamicMachine(RuleBasedStateMachine):
     def test_stuff(x):
         pass
 
+
... 300 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-hypothesis.git



More information about the Python-modules-commits mailing list