Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "everything but" recipe using from_type #1967

Merged
merged 13 commits into from
May 9, 2019
5 changes: 5 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RELEASE_TYPE: patch

Adds a recipe to the docstring of :func:`~hypothesis.strategies.from_type`
that describes a means for drawing values for "everything except" a specified type.
This recipe is especially useful for writing tests that perform input-type validation.
19 changes: 19 additions & 0 deletions hypothesis-python/src/hypothesis/_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,25 @@ def from_type(thing):
other elements in the lookup.
4. Finally, if ``thing`` has type annotations for all required arguments,
it is resolved via :func:`~hypothesis.strategies.builds`.

There is a valuable recipe for leveraging ``from_type()`` to generate
"everything except" values from a specified type. I.e.

.. code-block:: python

def everything_except(excluded_types):
return (
from_type(type).flatmap(from_type)
.filter(lambda x: not isinstance(x, excluded_types))
)

For example, ``everything_except(int)`` returns a strategy that can
generate anything that ``from_type()`` can ever generate, except for
instances of :class:python:int, and excluding instances of types
added via :func:~hypothesis.strategies.register_type_strategy.

This is useful when writing tests which check that invalid input is
rejected in a certain way.
"""
# TODO: We would like to move this to the top level, but pending some major
# refactoring it's hard to do without creating circular imports.
Expand Down
49 changes: 49 additions & 0 deletions hypothesis-python/tests/nocover/test_from_type_recipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# coding=utf-8
#
# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis/
#
# Most of this work is copyright (C) 2013-2019 David R. MacIver
# (david@drmaciver.com), but it contains contributions by others. See
# CONTRIBUTING.rst for a full list of people who may hold copyright, and
# consult the git log if you need to determine who owns an individual
# contribution.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
#
# END HEADER

from __future__ import absolute_import, division, print_function

import hypothesis.strategies as st
from hypothesis import given
from hypothesis.searchstrategy.types import _global_type_lookup


def everything_except(excluded_types):
"""Recipe copied from the docstring of ``from_type``"""
return (
st.from_type(type)
.flatmap(st.from_type)
.filter(lambda x: not isinstance(x, excluded_types))
)


@given(
excluded_types=st.lists(
st.sampled_from(
sorted(
[x for x in _global_type_lookup if x.__module__ != "typing"], key=str
)
),
min_size=1,
max_size=3,
unique=True,
).map(tuple),
data=st.data(),
)
def test_recipe_for_everything_except(excluded_types, data):
value = data.draw(everything_except(excluded_types))
assert not isinstance(value, excluded_types)