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

Tristan Seligmann mithrandi at moszumanska.debian.org
Mon Mar 7 04:02:20 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 61c11c29171cd875c52eb4cfa48359546eb8c32f
Author: Tristan Seligmann <mithrandi at debian.org>
Date:   Sun Mar 6 23:40:19 2016 +0200

    Import python-hypothesis_3.1.0.orig.tar.gz
---
 .travis.yml                                        |   3 +
 CONTRIBUTING.rst                                   |   1 +
 LICENSE.txt                                        |   4 +
 Makefile                                           |  12 +-
 README.rst                                         |  15 +-
 docs/changes.rst                                   |  41 +
 docs/django.rst                                    |  20 +
 scripts/install.sh                                 |   3 +
 src/hypothesis/__init__.py                         |   4 +-
 src/hypothesis/_settings.py                        |  55 +-
 src/hypothesis/core.py                             |  72 +-
 src/hypothesis/extra/django/models.py              |  11 +-
 src/hypothesis/extra/pytestplugin.py               |   9 +-
 src/hypothesis/internal/compat.py                  |  38 +-
 src/hypothesis/internal/conjecture/engine.py       | 141 ++--
 src/hypothesis/internal/conjecture/minimizer.py    |   8 -
 src/hypothesis/internal/reflection.py              |   9 +-
 src/hypothesis/reporting.py                        |   7 +-
 src/hypothesis/searchstrategy/collections.py       |   5 +-
 src/hypothesis/searchstrategy/deferred.py          |   4 +
 src/hypothesis/searchstrategy/flatmapped.py        |   3 +-
 src/hypothesis/searchstrategy/strategies.py        |  10 +-
 src/hypothesis/searchstrategy/wrappers.py          |   4 +
 src/hypothesis/stateful.py                         |  74 +-
 src/hypothesis/strategies.py                       | 107 ++-
 src/hypothesis/{version.py => vendor/__init__.py}  |   5 -
 src/hypothesis/vendor/pretty.py                    | 864 +++++++++++++++++++++
 src/hypothesis/version.py                          |   2 +-
 tests/common/utils.py                              |   5 +-
 tests/cover/test_conjecture_engine.py              |  17 +-
 tests/cover/test_database_usage.py                 |   2 +-
 tests/cover/test_deferred_errors.py                |   4 +-
 tests/cover/test_direct_strategies.py              |   1 -
 tests/cover/test_explicit_examples.py              |   6 +-
 tests/cover/test_health_checks.py                  |  25 +-
 tests/cover/test_nothing.py                        |  75 ++
 .../version.py => tests/cover/test_phases.py       |  24 +-
 tests/cover/test_pretty.py                         | 752 ++++++++++++++++++
 tests/cover/test_runner_strategy.py                |  71 ++
 tests/cover/test_searchstrategy.py                 |  10 +-
 tests/cover/test_simple_collections.py             |   8 +
 tests/cover/test_stateful.py                       |  46 +-
 tests/cover/test_testdecorators.py                 |  12 +-
 tests/datetime/test_datetime.py                    |   2 +
 tests/django/toystore/models.py                    |  16 +
 tests/django/toystore/test_given_models.py         |  24 +-
 tests/nocover/test_example_quality.py              |   8 +-
 tests/nocover/test_recursive.py                    |   2 +-
 tests/nocover/test_statistical_distribution.py     |   2 +-
 tests/py3/test_asyncio.py                          |  63 ++
 tox.ini                                            |   8 +
 51 files changed, 2535 insertions(+), 179 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index cae5ffe..f4abca4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,6 +29,7 @@ env:
         - TASK=check-pypy
         - TASK=check-py35
         - TASK=check-py27
+        - TASK=check-py273
         - TASK=check-py34
         - TASK=check-nose
         - TASK=check-pytest27
@@ -45,6 +46,8 @@ script:
 matrix:
     exclude:
         - os: osx
+          env: TASK=check-py273
+        - os: osx
           env: TASK=check-unicode
         - os: osx
           env: TASK=check-fakefactory052
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 7a6cffd..e364b30 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -143,6 +143,7 @@ their individual contributions.
 * `Derek Gustafson <https://www.github.com/degustaf>`_
 * `Florian Bruhin <https://www.github.com/The-Compiler>`_
 * `follower <https://www.github.com/follower>`_
+* `Jeremy Thurgood <https://github.com/jerith>`_
 * `Jonty Wareing <https://www.github.com/Jonty>`_ (`jonty at jonty.co.uk <mailto:jonty at jonty.co.uk>`_)
 * `kbara <https://www.github.com/kbara>`_
 * `Lee Begg <https://www.github.com/llnz2>`_
diff --git a/LICENSE.txt b/LICENSE.txt
index 496323d..2976939 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -2,3 +2,7 @@ Copyright (c) 2013, David R. MacIver
 
 All code in this repository except where explicitly noted otherwise is released
 under the Mozilla Public License v 2.0. You can obtain a copy at http://mozilla.org/MPL/2.0/.
+
+Some code in this repository comes from other projects. Where applicable, the
+original copyright and license are noted and any modifications made are released
+dual licensed with the original license.
diff --git a/Makefile b/Makefile
index b588631..aa4a367 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@ BUILD_RUNTIMES?=$(PWD)/.runtimes
 
 PY26=$(BUILD_RUNTIMES)/snakepit/python2.6
 PY27=$(BUILD_RUNTIMES)/snakepit/python2.7
+PY273=$(BUILD_RUNTIMES)/snakepit/python2.7.3
 PY33=$(BUILD_RUNTIMES)/snakepit/python3.3
 PY34=$(BUILD_RUNTIMES)/snakepit/python3.4
 PY35=$(BUILD_RUNTIMES)/snakepit/python3.5
@@ -39,6 +40,9 @@ $(PY26):
 $(PY27):
 	scripts/retry.sh scripts/install.sh 2.7
 
+$(PY273):
+	scripts/retry.sh scripts/install.sh 2.7.3
+
 $(PY33):
 	scripts/retry.sh scripts/install.sh 3.3
 
@@ -64,7 +68,7 @@ format: $(PYFORMAT) $(ISORT)
 	$(TOOL_PYTHON) scripts/enforce_header.py
 	# isort will sort packages differently depending on whether they're installed
 	$(ISORT_VIRTUALENV)/bin/python -m pip install django pytz pytest fake-factory numpy flaky
-	env -i PATH=$(PATH) $(ISORT) -p hypothesis -ls -m 2 -w 75 \
+	env -i PATH="$(PATH)" $(ISORT) -p hypothesis -ls -m 2 -w 75 \
 			-a  "from __future__ import absolute_import, print_function, division" \
 			-rc src tests examples
 	find src tests examples -name '*.py' | xargs $(PYFORMAT) -i
@@ -82,6 +86,9 @@ check-py26: $(PY26) $(TOX)
 check-py27: $(PY27) $(TOX)
 	$(TOX) -e py27-full
 
+check-py273: $(PY273) $(TOX)
+	$(TOX) -e oldpy27
+
 check-py33: $(PY33) $(TOX)
 	$(TOX) -e py33-full
 
@@ -145,10 +152,11 @@ check-fast: lint $(PY26) $(PY35) $(PYPY) $(TOX)
 	$(TOX) -e py35-prettyquick
 
 $(TOX): $(PY35) tox.ini $(TOOLS)
-	$(TOOL_INSTALL) tox
 	rm -f $(TOX)
+	$(TOOL_INSTALL) tox
 	rm -rf .tox
 	ln -sf $(TOOL_VIRTUALENV)/bin/tox $(TOX)
+	touch $(TOOL_VIRTUALENV)/bin/tox $(TOX)
 
 $(SPHINX_BUILD): $(TOOL_VIRTUALENV)
 	$(TOOL_PYTHON) -m pip install sphinx
diff --git a/README.rst b/README.rst
index f76d48f..45cde7c 100644
--- a/README.rst
+++ b/README.rst
@@ -11,9 +11,18 @@ Hypothesis is both extremely practical and also advances the state of the art of
 unit testing by some way. It's easy to use, stable, and extremely powerful. If
 you're not using Hypothesis to test your project then you're missing out.
 
-Hypothesis works with most widely used versions of Python. It officially supports
-CPython 2.7, 3.4 and 3.5, as well as PyPy. Most other versions of Python are known
-not to work.
+------------------
+Versions supported
+------------------
+
+Hypothesis officially supports CPython 2.7, 3.4 and 3.5, as well as PyPy. Other
+versions are not supported. Patches for such versions are accepted as long as
+they don't impose a maintenance burden. However, keep in mind that this does
+not automatically mean that future releases of Hypothesis will continue to work
+on those versions.
+
+See `Issue #286 <https://github.com/DRMacIver/hypothesis/issues/286>`_ for more
+information.
 
 -----------------
 Links of interest
diff --git a/docs/changes.rst b/docs/changes.rst
index 1792817..aee59d6 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -22,6 +22,47 @@ You should generally assume that an API is internal unless you have specific
 information to the contrary.
 
 ------------------
+3.1.0 - 2016-03-06
+------------------
+
+* Add a 'nothing' strategy that never successfully generates values.
+* sampled_from() and one_of() can both now be called with an empty argument
+  list, in which case they also never generate any values.
+* one_of may now be called with a single argument that is a collection of strategies
+  as well as as varargs.
+* Add a 'runner' strategy which returns the instance of the current test object
+  if there is one.
+* 'Bundle' for RuleBasedStateMachine is now a normal(ish) strategy and can be used
+  as such.
+* Tests using RuleBasedStateMachine should now shrink significantly better.
+* Hypothesis now uses a pretty-printing library internally, compatible with IPython's
+  pretty printing protocol (actually using the same code). This may improve the quality
+  of output in some cases.
+* As a 'phases' setting that allows more fine grained control over which parts of the
+  process Hypothesis runs
+* Add a suppress_health_check setting which allows you to turn off specific health checks
+  in a fine grained manner.
+* Fix a bug where lists of non fixed size would always draw one more element than they
+  included. This mostly didn't matter, but if would cause problems with empty strategies
+  or ones with side effects.
+* Add a mechanism to the Django model generator to allow you to explicitly request the
+  default value (thanks to Jeremy Thurgood for this one).
+
+------------------
+3.0.5 - 2016-02-25
+------------------
+
+* Fix a bug where Hypothesis would now error on py.test development versions.
+
+------------------
+3.0.4 - 2016-02-24
+------------------
+
+* Fix a bug where Hypothesis would error when running on Python 2.7.3 or
+  earlier because it was trying to pass a bytearray object to struct.unpack (
+  which is only supported since 2.7.4).
+
+------------------
 3.0.3 - 2016-02-23
 ------------------
 
diff --git a/docs/django.rst b/docs/django.rst
index 90c8f6f..e6f87d8 100644
--- a/docs/django.rst
+++ b/docs/django.rst
@@ -141,3 +141,23 @@ and then throw it away, instead returning the company we started for. This
 works because the models that Hypothesis generates are saved in the database,
 so we're essentially running the inner strategy purely for the side effect of
 creating those children in the database.
+
+
+Using default field values
+==========================
+
+Hypothesis ignores field defaults and always tries to generate values, even if
+it doesn't know how to. You can tell it to use the default value for a field
+instead of generating one by passing ``fieldname=default_value`` to
+``models()``:
+
+.. code:: python
+
+    >>> from toystore.models import DefaultCustomish
+    >>> models(DefaultCustomish).example()
+    hypothesis.errors.InvalidArgument: Missing arguments for mandatory field
+        customish for model DefaultCustomish
+    >>> from hypothesis.extra.django.models import default_value
+    >>> x = models(DefaultCustomish, customish=default_value).example()
+    >>> x.customish
+    'b'
diff --git a/scripts/install.sh b/scripts/install.sh
index 17ddc65..1d46107 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -86,6 +86,9 @@ for var in "$@"; do
     2.7)
       install 2.7.11 python2.7
       ;;
+    2.7.3)
+      install 2.7.3 python2.7.3
+      ;;
     3.2)
       install 3.2.6 python3.2
       ;;
diff --git a/src/hypothesis/__init__.py b/src/hypothesis/__init__.py
index 59e5ce3..cc6ef41 100644
--- a/src/hypothesis/__init__.py
+++ b/src/hypothesis/__init__.py
@@ -23,7 +23,7 @@ failing examples it finds.
 """
 
 
-from hypothesis._settings import settings, Verbosity
+from hypothesis._settings import settings, Verbosity, Phase, HealthCheck
 from hypothesis.version import __version_info__, __version__
 from hypothesis.control import assume, note, reject
 from hypothesis.core import given, find, example, seed
@@ -32,6 +32,8 @@ from hypothesis.core import given, find, example, seed
 __all__ = [
     'settings',
     'Verbosity',
+    'HealthCheck',
+    'Phase',
     'assume',
     'reject',
     'seed',
diff --git a/src/hypothesis/_settings.py b/src/hypothesis/_settings.py
index e51020f..1a73aee 100644
--- a/src/hypothesis/_settings.py
+++ b/src/hypothesis/_settings.py
@@ -27,6 +27,7 @@ import os
 import inspect
 import warnings
 import threading
+from enum import Enum, unique, IntEnum
 from collections import namedtuple
 
 from hypothesis.errors import InvalidArgument, HypothesisDeprecationWarning
@@ -144,6 +145,9 @@ class settings(settingsMeta('settings', (object,), {})):
             for setting in all_settings.values():
                 if kwargs.get(setting.name, not_set) is not_set:
                     kwargs[setting.name] = getattr(defaults, setting.name)
+                elif setting.validator:
+                    kwargs[setting.name] = setting.validator(
+                        kwargs[setting.name])
             if self._database is not_set:
                 self._database = defaults.database
         for name, value in kwargs.items():
@@ -172,6 +176,7 @@ class settings(settingsMeta('settings', (object,), {})):
     @classmethod
     def define_setting(
         cls, name, description, default, options=None, deprecation=None,
+        validator=None,
     ):
         """Add a new setting.
 
@@ -198,7 +203,8 @@ class settings(settingsMeta('settings', (object,), {})):
                 )
 
         all_settings[name] = Setting(
-            name, description.strip(), default, options, deprecation)
+            name, description.strip(), default, options, deprecation, validator
+        )
         setattr(settings, name, settingsProperty(name))
 
     @classmethod
@@ -327,7 +333,8 @@ class settings(settingsMeta('settings', (object,), {})):
 
 Setting = namedtuple(
     'Setting', (
-        'name', 'description', 'default', 'options', 'deprecation'))
+        'name', 'description', 'default', 'options', 'deprecation', 'validator'
+    ))
 
 
 settings.define_setting(
@@ -445,6 +452,24 @@ in which case no storage will be used.
 )
 
 
+ at unique
+class Phase(IntEnum):
+    explicit = 0
+    reuse = 1
+    generate = 2
+    shrink = 3
+
+
+ at unique
+class HealthCheck(Enum):
+    exception_in_generation = 0
+    data_too_large = 1
+    filter_too_much = 2
+    too_slow = 3
+    random_module = 4
+    return_value = 5
+
+
 class Verbosity(object):
 
     def __repr__(self):
@@ -507,6 +532,24 @@ settings.define_setting(
     description='Control the verbosity level of Hypothesis messages',
 )
 
+
+def _validate_phases(phases):
+    if phases is None:
+        return tuple(Phase)
+    phases = tuple(phases)
+    for a in phases:
+        if not isinstance(a, Phase):
+            raise InvalidArgument('%r is not a valid phase' % (a,))
+    return phases
+
+
+settings.define_setting(
+    'phases',
+    default=tuple(Phase),
+    description='Control which phases should be run',
+    validator=_validate_phases,
+)
+
 settings.define_setting(
     name='stateful_step_count',
     default=50,
@@ -523,6 +566,14 @@ If set to True, Hypothesis will run a preliminary health check before
 attempting to actually execute your test.
 """
 )
+
+settings.define_setting(
+    'suppress_health_check',
+    default=[],
+    description="""A list of health checks to disable"""
+)
+
+
 settings.lock_further_definitions()
 
 settings.register_profile('default', settings())
diff --git a/src/hypothesis/core.py b/src/hypothesis/core.py
index bd97b1e..824de6a 100644
--- a/src/hypothesis/core.py
+++ b/src/hypothesis/core.py
@@ -32,13 +32,13 @@ from hypothesis.errors import Flaky, Timeout, NoSuchExample, \
     UnsatisfiedAssumption, HypothesisDeprecationWarning
 from hypothesis.control import BuildContext
 from hypothesis._settings import settings as Settings
-from hypothesis._settings import Verbosity
+from hypothesis._settings import Phase, Verbosity, HealthCheck
 from hypothesis.executors import new_style_executor, \
     default_new_style_executor
 from hypothesis.reporting import report, verbose_report, current_verbosity
 from hypothesis.internal.compat import getargspec, str_to_bytes
-from hypothesis.internal.reflection import arg_string, impersonate, \
-    copy_argspec, function_digest, fully_qualified_name, \
+from hypothesis.internal.reflection import nicerepr, arg_string, \
+    impersonate, copy_argspec, function_digest, fully_qualified_name, \
     convert_positional_arguments, get_pretty_function_description
 from hypothesis.searchstrategy.strategies import SearchStrategy
 
@@ -120,6 +120,18 @@ def seed(seed):
     return accept
 
 
+class WithRunner(SearchStrategy):
+
+    def __init__(self, base, runner):
+        assert runner is not None
+        self.base = base
+        self.runner = runner
+
+    def do_draw(self, data):
+        data.hypothesis_runner = self.runner
+        return self.base.do_draw(data)
+
+
 def given(*generator_arguments, **generator_kwargs):
     """A decorator for turning a test function that accepts arguments into a
     randomized test.
@@ -238,6 +250,8 @@ def given(*generator_arguments, **generator_kwargs):
                     ))
                 else:
                     example_kwargs = example.kwargs
+                if Phase.explicit not in settings.phases:
+                    continue
                 example_kwargs.update(kwargs)
                 # Note: Test may mutate arguments and we can't rerun explicit
                 # examples, so we have to calculate the failure message at this
@@ -268,14 +282,23 @@ def given(*generator_arguments, **generator_kwargs):
                 )
             )
 
-            def fail_health_check(message):
+            def fail_health_check(message, label):
+                if label in settings.suppress_health_check:
+                    return
                 message += (
                     '\nSee http://hypothesis.readthedocs.org/en/latest/health'
-                    'checks.html for more information about this.'
+                    'checks.html for more information about this. '
                 )
+                message += (
+                    'If you want to disable just this health check, add %s'
+                    'to the suppress_health_check settings for this test.'
+                ) % (label,)
                 raise FailedHealthCheck(message)
 
             search_strategy = given_specifier
+            if selfy is not None:
+                search_strategy = WithRunner(search_strategy, selfy)
+
             search_strategy.validate()
 
             perform_health_check = settings.perform_health_check
@@ -283,6 +306,11 @@ def given(*generator_arguments, **generator_kwargs):
 
             from hypothesis.internal.conjecture.data import TestData, Status, \
                 StopTest
+            if not (
+                Phase.reuse in settings.phases or
+                Phase.generate in settings.phases
+            ):
+                return
 
             if perform_health_check:
                 initial_state = getglobalrandomstate()
@@ -332,6 +360,8 @@ def given(*generator_arguments, **generator_kwargs):
                         else:
                             assert data.status == Status.OVERRUN
                             overruns += 1
+                    except InvalidArgument:
+                        raise
                     except Exception:
                         report(traceback.format_exc())
                         if test_runner is default_new_style_executor:
@@ -341,7 +371,8 @@ def given(*generator_arguments, **generator_kwargs):
                                 'This indicates a bug in the strategy. '
                                 'This could either be a Hypothesis bug or '
                                 "an error in a function yo've passed to "
-                                'it to construct your data.'
+                                'it to construct your data.',
+                                HealthCheck.exception_in_generation,
                             )
                         else:
                             fail_health_check(
@@ -353,7 +384,8 @@ def given(*generator_arguments, **generator_kwargs):
                                 'it to construct your data. Additionally, '
                                 'you have a custom executor, which means '
                                 'that this could be your executor failing '
-                                'to handle a function which returns None. '
+                                'to handle a function which returns None. ',
+                                HealthCheck.exception_in_generation,
                             )
                 if overruns >= 20 or (
                     not count and overruns > 0
@@ -366,7 +398,7 @@ def given(*generator_arguments, **generator_kwargs):
                         'max_size parameters on your collections and turning '
                         'max_leaves down on recursive() calls.') % (
                         overruns, count
-                    ))
+                    ), HealthCheck.data_too_large)
                 if filtered_draws >= 50 or (
                     not count and filtered_draws > 0
                 ):
@@ -379,7 +411,7 @@ def given(*generator_arguments, **generator_kwargs):
                         'strategy to filter less. This can also be caused by '
                         'a low max_leaves parameter in recursive() calls') % (
                         filtered_draws, count
-                    ))
+                    ), HealthCheck.filter_too_much)
                 runtime = time.time() - start
                 if runtime > 1.0 or count < 10:
                     fail_health_check((
@@ -388,7 +420,9 @@ def given(*generator_arguments, **generator_kwargs):
                         'and %d exceeded maximum size). Try decreasing '
                         "size of the data you're generating (with e.g."
                         'average_size or max_leaves parameters).'
-                    ) % (count, runtime, filtered_draws, overruns))
+                    ) % (count, runtime, filtered_draws, overruns),
+                        HealthCheck.too_slow,
+                    )
                 if getglobalrandomstate() != initial_state:
                     fail_health_check(
                         'Data generation depends on global random module. '
@@ -398,7 +432,7 @@ def given(*generator_arguments, **generator_kwargs):
                         'randoms() from hypothesis.strategies to get an '
                         'instance of Random you can use. Alternatively, you '
                         'can use the random_module() strategy to explicitly '
-                        'seed the random module.'
+                        'seed the random module.', HealthCheck.random_module,
                     )
             last_exception = [None]
             repr_for_last_exception = [None]
@@ -415,10 +449,10 @@ def given(*generator_arguments, **generator_kwargs):
                         search_strategy, test,
                     ))
                     if result is not None and settings.perform_health_check:
-                        raise FailedHealthCheck((
+                        fail_health_check((
                             'Tests run under @given should return None, but '
                             '%s returned %r instead.'
-                        ) % (test.__name__, result), settings)
+                        ) % (test.__name__, result), HealthCheck.return_value)
                     return False
                 except UnsatisfiedAssumption:
                     data.mark_invalid()
@@ -442,7 +476,9 @@ def given(*generator_arguments, **generator_kwargs):
                             'consider using the randoms() strategy from '
                             'hypothesis.strategies instead. Alternatively, '
                             'you can use the random_module() strategy to '
-                            'explicitly seed the random module.')
+                            'explicitly seed the random module.',
+                            HealthCheck.random_module,
+                        )
 
             from hypothesis.internal.conjecture.engine import TestRunner
 
@@ -460,6 +496,8 @@ def given(*generator_arguments, **generator_kwargs):
                 settings.timeout > 0 and
                 run_time >= settings.timeout
             )
+            if runner.last_data is None:
+                return
             if runner.last_data.status == Status.INTERESTING:
                 falsifying_example = runner.last_data.buffer
                 if settings.database is not None:
@@ -584,16 +622,16 @@ def find(specifier, condition, settings=None, random=None, database_key=None):
         if settings.verbosity == Verbosity.verbose:
             if not successful_examples[0]:
                 report(lambda: u'Trying example %s' % (
-                    repr(result),
+                    nicerepr(result),
                 ))
             elif success:
                 if successful_examples[0] == 1:
                     report(lambda: u'Found satisfying example %s' % (
-                        repr(result),
+                        nicerepr(result),
                     ))
                 else:
                     report(lambda: u'Shrunk example to %s' % (
-                        repr(result),
+                        nicerepr(result),
                     ))
                 last_data[0] = data
         if success and not data.frozen:
diff --git a/src/hypothesis/extra/django/models.py b/src/hypothesis/extra/django/models.py
index 04d287d..f9eaf03 100644
--- a/src/hypothesis/extra/django/models.py
+++ b/src/hypothesis/extra/django/models.py
@@ -23,6 +23,7 @@ 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
 from hypothesis.searchstrategy.strategies import SearchStrategy
 
 
@@ -72,6 +73,9 @@ def add_default_field_mapping(field_type, strategy):
     field_mappings()[field_type] = strategy
 
 
+default_value = UniqueIdentifier(u'default_value')
+
+
 def models(model, **extra):
     result = {}
     mappings = field_mappings()
@@ -96,12 +100,9 @@ def models(model, **extra):
                 u', '.join(missed),
                 model.__name__,
             )))
-    for k, v in extra.items():
-        if isinstance(v, SearchStrategy):
-            result[k] = v
-        else:
-            result[k] = st.just(v)
     result.update(extra)
+    # Remove default_values so we don't try to generate anything for those.
+    result = {k: v for k, v in result.items() if v is not default_value}
     return ModelStrategy(model, result)
 
 
diff --git a/src/hypothesis/extra/pytestplugin.py b/src/hypothesis/extra/pytestplugin.py
index 12cc547..e040e46 100644
--- a/src/hypothesis/extra/pytestplugin.py
+++ b/src/hypothesis/extra/pytestplugin.py
@@ -22,11 +22,10 @@ import pytest
 
 from hypothesis.reporting import default as default_reporter
 
-PYTEST_VERSION = tuple(
-    map(
-        int,
-        re.sub('-.+', '', pytest.__version__).split('.')
-    ))[:3]
+PYTEST_VERSION = tuple(map(
+    int,
+    re.sub('-.+', '', pytest.__version__).split('.')[:3]
+))
 
 LOAD_PROFILE_OPTION = '--hypothesis-profile'
 
diff --git a/src/hypothesis/internal/compat.py b/src/hypothesis/internal/compat.py
index 49c7934..58ec3ce 100644
--- a/src/hypothesis/internal/compat.py
+++ b/src/hypothesis/internal/compat.py
@@ -41,6 +41,7 @@ PYPY = platform.python_implementation() == 'PyPy'
 PY26 = sys.version_info[:2] == (2, 6)
 NO_ARGSPEC = sys.version_info[:2] >= (3, 5)
 HAS_SIGNATURE = sys.version_info[:2] >= (3, 3)
+CAN_UNPACK_BYTE_ARRAY = sys.version_info[:3] >= (2, 7, 4)
 
 WINDOWS = platform.system() == 'Windows'
 
@@ -95,6 +96,9 @@ if PY3:
     def escape_unicode_characters(s):
         return codecs.encode(s, 'unicode_escape').decode('ascii')
 
+    def print_unicode(x):
+        print(x)
+
     exec("""
 def quiet_raise(exc):
     raise exc from None
@@ -122,11 +126,15 @@ else:
 
     def int_from_bytes(data):
         assert isinstance(data, bytearray)
+        if CAN_UNPACK_BYTE_ARRAY:
+            unpackable_data = data
+        else:
+            unpackable_data = bytes(data)
         result = 0
         i = 0
         while i + 4 <= len(data):
             result <<= 32
-            result |= struct.unpack('>I', data[i:i + 4])[0]
+            result |= struct.unpack('>I', unpackable_data[i:i + 4])[0]
             i += 4
         while i < len(data):
             result <<= 8
@@ -215,6 +223,11 @@ else:
     def escape_unicode_characters(s):
         return codecs.encode(s, 'string_escape')
 
+    def print_unicode(x):
+        if isinstance(x, unicode):
+            x = x.encode(a_good_encoding())
+        print(x)
+
     def quiet_raise(exc):
         raise exc
 
@@ -414,6 +427,29 @@ class compatbytes(bytearray):
 if PY2:
     hbytes = compatbytes
     reasonable_byte_type = bytearray
+    string_types = (str, unicode)
 else:
     hbytes = bytes
     reasonable_byte_type = bytes
+    string_types = (str,)
+
+
+if PY2:
+    def to_str(s):
+        if isinstance(s, unicode):
+            return s.encode(a_good_encoding())
+        assert isinstance(s, str)
+        return s
+else:
+    def to_str(s):
+        return s
+
+
+def cast_unicode(s, encoding=None):
+    if isinstance(s, bytes):
+        return s.decode(encoding or a_good_encoding(), 'replace')
+    return s
+
+
+def get_stream_enc(stream, default=None):
+    return getattr(stream, 'encoding', None) or default
diff --git a/src/hypothesis/internal/conjecture/engine.py b/src/hypothesis/internal/conjecture/engine.py
index cbb72d8..5f16aa1 100644
--- a/src/hypothesis/internal/conjecture/engine.py
+++ b/src/hypothesis/internal/conjecture/engine.py
@@ -20,6 +20,7 @@ import time
 from random import Random, getrandbits
 
 from hypothesis import settings as Settings
+from hypothesis import Phase
 from hypothesis.reporting import debug_report
 from hypothesis.internal.compat import hbytes, hrange, Counter, \
     text_type, bytes_from_list, to_bytes_sequence, unicode_safe_repr
@@ -48,6 +49,7 @@ class TestRunner(object):
         self.start_time = time.time()
         self.random = random or Random(getrandbits(128))
         self.database_key = database_key
+        self.seen = set()
 
     def new_buffer(self):
         self.last_data = TestData(
@@ -71,6 +73,13 @@ class TestRunner(object):
         except:
             self.save_buffer(data.buffer)
             raise
+        if (
+            data.status == Status.INTERESTING and (
+                self.last_data is None or
+                data.buffer != self.last_data.buffer
+            )
+        ):
+            self.debug_data(data)
         if data.status >= Status.VALID:
             self.valid_examples += 1
 
@@ -80,6 +89,9 @@ class TestRunner(object):
         #   2. Any transition which increases the status is valid
         #   3. If the previous status was interesting, only shrinking
         #      transitions are allowed.
+        self.seen.add(hbytes(data.buffer))
+        if data.buffer == self.last_data.buffer:
+            return False
         if self.last_data.status < data.status:
             return True
         if self.last_data.status > data.status:
@@ -98,7 +110,8 @@ class TestRunner(object):
     def save_buffer(self, buffer):
         if (
             self.settings.database is not None and
-            self.database_key is not None
+            self.database_key is not None and
+            Phase.reuse in self.settings.phases
         ):
             self.settings.database.save(
                 self.database_key, hbytes(buffer)
@@ -121,6 +134,8 @@ class TestRunner(object):
         ))
 
     def incorporate_new_buffer(self, buffer):
+        if buffer in self.seen:
+            return False
         assert self.last_data.status == Status.INTERESTING
         if (
             self.settings.timeout > 0 and
@@ -136,8 +151,6 @@ class TestRunner(object):
         self.test_function(data)
         data.freeze()
         self.note_for_corpus(data)
-        if data.status >= self.last_data.status:
-            self.debug_data(data)
         if self.consider_new_test_data(data):
             self.shrinks += 1
             self.last_data = data
@@ -265,54 +278,59 @@ class TestRunner(object):
                     self.last_data = data
                     break
 
-        if (
-            self.last_data is None or
-            self.last_data.status < Status.INTERESTING
-        ):
-            self.new_buffer()
-        mutator = self._new_mutator()
-        while self.last_data.status != Status.INTERESTING:
-            if self.valid_examples >= self.settings.max_examples:
-                return
-            if self.iterations >= max(
-                self.settings.max_iterations, self.settings.max_examples
-            ):
-                return
+        if Phase.generate in self.settings.phases:
             if (
-                self.settings.timeout > 0 and
-                time.time() >= start_time + self.settings.timeout
+                self.last_data is None or
+                self.last_data.status < Status.INTERESTING
             ):
-                return
-            if mutations >= self.settings.max_mutations:
-                mutations = 0
                 self.new_buffer()
-                mutator = self._new_mutator()
-            else:
-                data = TestData(
-                    draw_bytes=mutator,
-                    max_length=self.settings.buffer_size
-                )
-                self.test_function(data)
-                data.freeze()
-                self.note_for_corpus(data)
-                prev_data = self.last_data
-                if self.consider_new_test_data(data):
-                    self.last_data = data
-                    if data.status > prev_data.status:
-                        mutations = 0
-                else:
-                    mutator = self._new_mutator()
 
-            mutations += 1
+            mutator = self._new_mutator()
+            while self.last_data.status != Status.INTERESTING:
+                if self.valid_examples >= self.settings.max_examples:
+                    return
+                if self.iterations >= max(
+                    self.settings.max_iterations, self.settings.max_examples
+                ):
+                    return
+                if (
+                    self.settings.timeout > 0 and
+                    time.time() >= start_time + self.settings.timeout
+                ):
+                    return
+                if mutations >= self.settings.max_mutations:
+                    mutations = 0
+                    self.new_buffer()
+                    mutator = self._new_mutator()
+                else:
+                    data = TestData(
+                        draw_bytes=mutator,
+                        max_length=self.settings.buffer_size
+                    )
+                    self.test_function(data)
+                    data.freeze()
+                    self.note_for_corpus(data)
+                    prev_data = self.last_data
+                    if self.consider_new_test_data(data):
+                        self.last_data = data
+                        if data.status > prev_data.status:
+                            mutations = 0
+                    else:
+                        mutator = self._new_mutator()
+
+                mutations += 1
 
         data = self.last_data
+        if data is None:
+            return
         assert isinstance(data.output, text_type)
 
-        self.debug_data(data)
-
         if self.settings.max_shrinks <= 0:
             return
 
+        if Phase.shrink not in self.settings.phases:
+            return
+
         if not self.last_data.buffer:
             return
 
@@ -325,6 +343,24 @@ class TestRunner(object):
 
         while self.changed > change_counter:
             change_counter = self.changed
+            failed_deletes = 0
+            while self.last_data.intervals and failed_deletes < 10:
+                if self.random.randint(0, 1):
+                    u, v = self.random.choice(self.last_data.intervals)
+                else:
+                    n = len(self.last_data.buffer) - 1
+                    u, v = sorted((
+                        self.random.choice(self.last_data.intervals)
+                    ))
+                if (
+                    v < len(self.last_data.buffer)
+                ) and self.incorporate_new_buffer(
+                    self.last_data.buffer[:u] +
+                    self.last_data.buffer[v:]
+                ):
+                    failed_deletes = 0
+                else:
+                    failed_deletes += 1
             i = 0
             while i < len(self.last_data.intervals):
                 u, v = self.last_data.intervals[i]
@@ -334,6 +370,13 @@ class TestRunner(object):
                 ):
                     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
+            i = 0
             while i < len(self.last_data.blocks):
                 u, v = self.last_data.blocks[i]
                 buf = self.last_data.buffer
@@ -355,19 +398,25 @@ class TestRunner(object):
             while block_counter < self.changed:
                 block_counter = self.changed
                 blocks = [
-                    k for k, v in
+                    k for k, count in
                     Counter(
                         self.last_data.buffer[u:v]
                         for u, v in self.last_data.blocks).items()
-                    if v > 1
+                    if count > 1
                 ]
                 for block in blocks:
-                    parts = self.last_data.buffer.split(block)
-                    assert self.last_data.buffer == block.join(parts)
+                    parts = [
+                        self.last_data.buffer[r:s]
+                        for r, s in self.last_data.blocks
+                    ]
+
+                    def replace(b):
+                        return b''.join(
+                            bytes(b if c == block else c) for c in parts
+                        )
                     minimize(
                         block,
-                        lambda b: self.incorporate_new_buffer(
-                            b.join(parts)),
+                        lambda b: self.incorporate_new_buffer(replace(b)),
                         self.random
                     )
 
... 2991 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