Beispiel #1
0
    def run_state_machine(factory, data):
        machine = factory()
        check_type(GenericStateMachine, machine, "state_machine_factory()")
        data.conjecture_data.hypothesis_runner = machine

        n_steps = settings.stateful_step_count
        should_continue = cu.many(data.conjecture_data,
                                  min_size=1,
                                  max_size=n_steps,
                                  average_size=n_steps)

        print_steps = (current_build_context().is_final
                       or current_verbosity() >= Verbosity.debug)
        try:
            if print_steps:
                machine.print_start()
            machine.check_invariants()

            while should_continue.more():
                value = data.conjecture_data.draw(machine.steps())
                if print_steps:
                    machine.print_step(value)
                machine.execute_step(value)
                machine.check_invariants()
        finally:
            if print_steps:
                machine.print_end()
            machine.teardown()
Beispiel #2
0
def times(
    min_value: dt.time = dt.time.min,
    max_value: dt.time = dt.time.max,
    *,
    timezones: SearchStrategy[Optional[dt.tzinfo]] = none()
) -> SearchStrategy[dt.time]:
    """times(min_value=datetime.time.min, max_value=datetime.time.max, *, timezones=none())

    A strategy for times between ``min_value`` and ``max_value``.

    The ``timezones`` argument is handled as for :py:func:`datetimes`.

    Examples from this strategy shrink towards midnight, with the timezone
    component shrinking as for the strategy that provided it.
    """
    check_type(dt.time, min_value, "min_value")
    check_type(dt.time, max_value, "max_value")
    if min_value.tzinfo is not None:
        raise InvalidArgument("min_value=%r must not have tzinfo" % min_value)
    if max_value.tzinfo is not None:
        raise InvalidArgument("max_value=%r must not have tzinfo" % max_value)
    check_valid_interval(min_value, max_value, "min_value", "max_value")
    day = dt.date(2000, 1, 1)
    return datetimes(
        min_value=dt.datetime.combine(day, min_value),
        max_value=dt.datetime.combine(day, max_value),
        timezones=timezones,
    ).map(lambda t: t.timetz())
Beispiel #3
0
def staticfunctions(draw,
		parent=classes(),
		min_argc=None, # int
		max_argc=None, # int
		manual_argument_bindings=None, # {}
		manual_keyword_bindings=None, # {}
		kwarginit=hs.nothing(),
		decorators=None, # [] itterable
		body=_phony_callable,
	):
	"""DOCUMENT ME!!!"""
	check_strategy(parent, name="parent")
	check_type(list, decorators, name="decorators")

	# primary decorator must be first in the series function properly
	decorators = [staticmethod,] + decorators

	container = draw(parent)
	method_body = draw(functions(
		binding_regex=binding_regex,
		min_argc=min_argc,
		max_argc=max_argc,
		manual_argument_bindings=arguments,
		manual_keyword_bindings=manual_keyword_bindings,
		kwarginit=kwarginit,
		decorators=decorators,
		body=body,
	))
	setattr(container, method_body.__name__, method_body)

	call = "".join(["container.", method_body.__name__, "(*args, **kwargs)"])
	def function_wrapper(*args, **kwargs):
		exec(call , locals())

	return function_wrapper
Beispiel #4
0
def timezones(*,
              no_cache: bool = False) -> SearchStrategy["zoneinfo.ZoneInfo"]:
    """A strategy for :class:`python:zoneinfo.ZoneInfo` objects.

    If ``no_cache=True``, the generated instances are constructed using
    :meth:`ZoneInfo.no_cache <python:zoneinfo.ZoneInfo.no_cache>` instead
    of the usual constructor.  This may change the semantics of your datetimes
    in surprising ways, so only use it if you know that you need to!

    .. note::

        The :mod:`python:zoneinfo` module is new in Python 3.9, so you will need
        to install the :pypi:`backports.zoneinfo` module on earlier versions, and
        the :pypi:`importlib_resources` backport on Python 3.6.

        ``pip install hypothesis[zoneinfo]`` will install these conditional
        dependencies if and only if they are needed.
    """
    check_type(bool, no_cache, "no_cache")
    if zoneinfo is None:  # pragma: no cover
        raise ModuleNotFoundError(
            "The zoneinfo module is required, but could not be imported.  "
            "Run `pip install hypothesis[zoneinfo]` and try again.")
    return timezone_keys().map(
        zoneinfo.ZoneInfo.no_cache if no_cache else zoneinfo.ZoneInfo)
Beispiel #5
0
def classes(draw, name=None, inherits=None, children=None):
	"""DOCUMENT ME!!!"""
	# double check types because insurance :P (and hypothesis standards lol)
	check_type(text_type, name, "name")
	check_type(list, inherits, "inherits")

	if children is None:
		children = draw(hs.dictionaries(
			hs.from_regex(_supported_binding_regex),
			_strategies()
		))

		bindings = children.keys()
		values = children.values()
	else:
		bindings, values = _validate_bindings(draw, children, name="children")

	class_name = draw(hs.from_regex(
		name if name is not None else _supported_binding_regex
	))
 	code = "".join([
		"class ", class_name, "(*inherits):\n\t", "\n\t".join(
			["pass"] if len(members) < 1 else [
				"%s = tallent[%r]" % (name, pos) \
					for pos, name in enumerate(members)
			]
		)
	])

	exec(code, locals())

	return locals()[class_name]
Beispiel #6
0
def indexes(*,
            elements: st.SearchStrategy[Ex] = None,
            dtype: Any = None,
            min_size: int = 0,
            max_size: int = None,
            unique: bool = True) -> st.SearchStrategy[pandas.Index]:
    """Provides a strategy for producing a :class:`pandas.Index`.

    Arguments:

    * elements is a strategy which will be used to generate the individual
      values of the index. If None, it will be inferred from the dtype. Note:
      even if the elements strategy produces tuples, the generated value
      will not be a MultiIndex, but instead be a normal index whose elements
      are tuples.
    * dtype is the dtype of the resulting index. If None, it will be inferred
      from the elements strategy. At least one of dtype or elements must be
      provided.
    * min_size is the minimum number of elements in the index.
    * max_size is the maximum number of elements in the index. If None then it
      will default to a suitable small size. If you want larger indexes you
      should pass a max_size explicitly.
    * unique specifies whether all of the elements in the resulting index
      should be distinct.
    """
    check_valid_size(min_size, "min_size")
    check_valid_size(max_size, "max_size")
    check_valid_interval(min_size, max_size, "min_size", "max_size")
    check_type(bool, unique, "unique")

    elements, dtype = elements_and_dtype(elements, dtype)

    if max_size is None:
        max_size = min_size + DEFAULT_MAX_SIZE
    return ValueIndexStrategy(elements, dtype, min_size, max_size, unique)
Beispiel #7
0
def binary_operation(
    func: Callable[[X, X], Y],
    *,
    associative: bool = True,
    commutative: bool = True,
    identity: Union[X, InferType, None] = infer,
    distributes_over: Callable[[X, X], X] = None,
    except_: Except = (),
    style: str = "pytest",
) -> str:
    """Write property tests for the binary operation ``func``.

    While :wikipedia:`binary operations <Binary_operation>` are not particularly
    common, they have such nice properties to test that it seems a shame not to
    demonstrate them with a ghostwriter.  For an operator `f`, test that:

    - if :wikipedia:`associative <Associative_property>`,
      ``f(a, f(b, c)) == f(f(a, b), c)``
    - if :wikipedia:`commutative <Commutative_property>`, ``f(a, b) == f(b, a)``
    - if :wikipedia:`identity <Identity_element>` is not None, ``f(a, identity) == a``
    - if :wikipedia:`distributes_over <Distributive_property>` is ``+``,
      ``f(a, b) + f(a, c) == f(a, b+c)``

    For example:

    .. code-block:: python

        ghostwriter.binary_operation(
            operator.mul,
            identity=1,
            inverse=operator.div,
            distributes_over=operator.add,
            style="unittest",
        )
    """
    if not callable(func):
        raise InvalidArgument(f"Got non-callable func={func!r}")
    except_ = _check_except(except_)
    _check_style(style)
    check_type(bool, associative, "associative")
    check_type(bool, commutative, "commutative")
    if distributes_over is not None and not callable(distributes_over):
        raise InvalidArgument(
            f"distributes_over={distributes_over!r} must be an operation which "
            f"distributes over {func.__name__}"
        )
    if not any([associative, commutative, identity, distributes_over]):
        raise InvalidArgument(
            "You must select at least one property of the binary operation to test."
        )
    imports, body = _make_binop_body(
        func,
        associative=associative,
        commutative=commutative,
        identity=identity,
        distributes_over=distributes_over,
        except_=except_,
        style=style,
    )
    return _make_test(imports, body)
def run_state_machine_as_test(state_machine_factory, settings=None):
    """Run a state machine definition as a test, either silently doing nothing
    or printing a minimal breaking program and raising an exception.

    state_machine_factory is anything which returns an instance of
    GenericStateMachine when called with no arguments - it can be a class or a
    function. settings will be used to control the execution of the test.
    """
    if settings is None:
        try:
            settings = state_machine_factory.TestCase.settings
            check_type(Settings, settings, "state_machine_factory.TestCase.settings")
        except AttributeError:
            settings = Settings(deadline=None, suppress_health_check=HealthCheck.all())
    check_type(Settings, settings, "settings")

    @settings
    @given(st.data())
    def run_state_machine(factory, data):
        machine = factory()
        check_type(GenericStateMachine, machine, "state_machine_factory()")
        data.conjecture_data.hypothesis_runner = machine

        n_steps = settings.stateful_step_count
        should_continue = cu.many(
            data.conjecture_data, min_size=1, max_size=n_steps, average_size=n_steps
        )

        print_steps = (
            current_build_context().is_final or current_verbosity() >= Verbosity.debug
        )
        try:
            if print_steps:
                machine.print_start()
            machine.check_invariants()

            while should_continue.more():
                value = data.conjecture_data.draw(machine.steps())
                if print_steps:
                    machine.print_step(value)
                machine.execute_step(value)
                machine.check_invariants()
        finally:
            if print_steps:
                machine.print_end()
            machine.teardown()

    # Use a machine digest to identify stateful tests in the example database
    run_state_machine.hypothesis.inner_test._hypothesis_internal_add_digest = function_digest(
        state_machine_factory
    )
    # Copy some attributes so @seed and @reproduce_failure "just work"
    run_state_machine._hypothesis_internal_use_seed = getattr(
        state_machine_factory, "_hypothesis_internal_use_seed", None
    )
    run_state_machine._hypothesis_internal_use_reproduce_failure = getattr(
        state_machine_factory, "_hypothesis_internal_use_reproduce_failure", None
    )

    run_state_machine(state_machine_factory)
Beispiel #9
0
def integer_array_indices(shape, result_shape=array_shapes(), dtype="int"):
    # type: (Shape, SearchStrategy[Shape], np.dtype) -> st.SearchStrategy[Tuple[np.ndarray, ...]]
    """Return a search strategy for tuples of integer-arrays that, when used
    to index into an array of shape ``shape``, given an array whose shape
    was drawn from ``result_shape``.

    Examples from this strategy shrink towards the tuple of index-arrays::

        len(shape) * (np.zeros(drawn_result_shape, dtype), )

    * ``shape`` a tuple of integers that indicates the shape of the array,
      whose indices are being generated.
    * ``result_shape`` a strategy for generating tuples of integers, which
      describe the shape of the resulting index arrays. The default is
      :func:`~hypothesis.extra.numpy.array_shapes`.  The shape drawn from
      this strategy determines the shape of the array that will be produced
      when the corresponding example from ``integer_array_indices`` is used
      as an index.
    * ``dtype`` the integer data type of the generated index-arrays. Negative
      integer indices can be generated if a signed integer type is specified.

    Recall that an array can be indexed using a tuple of integer-arrays to
    access its members in an arbitrary order, producing an array with an
    arbitrary shape. For example:

    .. code-block:: pycon

        >>> from numpy import array
        >>> x = array([-0, -1, -2, -3, -4])
        >>> ind = (array([[4, 0], [0, 1]]),)  # a tuple containing a 2D integer-array
        >>> x[ind]  # the resulting array is commensurate with the indexing array(s)
        array([[-4,  0],
               [0, -1]])

    Note that this strategy does not accommodate all variations of so-called
    'advanced indexing', as prescribed by NumPy's nomenclature.  Combinations
    of basic and advanced indexes are too complex to usefully define in a
    standard strategy; we leave application-specific strategies to the user.
    Advanced-boolean indexing can be defined as ``arrays(shape=..., dtype=bool)``,
    and is similarly left to the user.
    """
    check_type(tuple, shape, "shape")
    check_argument(
        shape and all(isinstance(x, integer_types) and x > 0 for x in shape),
        "shape=%r must be a non-empty tuple of integers > 0" % (shape, ),
    )
    check_type(SearchStrategy, result_shape, "result_shape")
    check_argument(np.issubdtype(dtype, np.integer),
                   "dtype=%r must be an integer dtype" % (dtype, ))
    signed = np.issubdtype(dtype, np.signedinteger)

    def array_for(index_shape, size):
        return arrays(
            dtype=dtype,
            shape=index_shape,
            elements=st.integers(-size if signed else 0, size - 1),
        )

    return result_shape.flatmap(lambda index_shape: st.tuples(
        *[array_for(index_shape, size) for size in shape]))
Beispiel #10
0
 def get_profile(name: str) -> "settings":
     """Return the profile with the given name."""
     check_type(str, name, "name")
     try:
         return settings._profiles[name]
     except KeyError:
         raise InvalidArgument(f"Profile {name!r} is not registered") from None
Beispiel #11
0
def target(observation: Union[int, float],
           *,
           label: str = "") -> Union[int, float]:
    """Calling this function with an ``int`` or ``float`` observation gives it feedback
    with which to guide our search for inputs that will cause an error, in
    addition to all the usual heuristics.  Observations must always be finite.

    Hypothesis will try to maximize the observed value over several examples;
    almost any metric will work so long as it makes sense to increase it.
    For example, ``-abs(error)`` is a metric that increases as ``error``
    approaches zero.

    Example metrics:

    - Number of elements in a collection, or tasks in a queue
    - Mean or maximum runtime of a task (or both, if you use ``label``)
    - Compression ratio for data (perhaps per-algorithm or per-level)
    - Number of steps taken by a state machine

    The optional ``label`` argument can be used to distinguish between
    and therefore separately optimise distinct observations, such as the
    mean and standard deviation of a dataset.  It is an error to call
    ``target()`` with any label more than once per test case.

    .. note::
        **The more examples you run, the better this technique works.**

        As a rule of thumb, the targeting effect is noticeable above
        :obj:`max_examples=1000 <hypothesis.settings.max_examples>`,
        and immediately obvious by around ten thousand examples
        *per label* used by your test.

    :ref:`statistics` include the best score seen for each label,
    which can help avoid `the threshold problem
    <https://hypothesis.works/articles/threshold-problem/>`__ when the minimal
    example shrinks right down to the threshold of failure (:issue:`2180`).
    """
    check_type((int, float), observation, "observation")
    if not math.isfinite(observation):
        raise InvalidArgument(
            f"observation={observation!r} must be a finite float.")
    check_type(str, label, "label")

    context = _current_build_context.value
    if context is None:
        raise InvalidArgument(
            "Calling target() outside of a test is invalid.  "
            "Consider guarding this call with `if currently_in_test_context(): ...`"
        )
    verbose_report(f"Saw target(observation={observation!r}, label={label!r})")

    if label in context.data.target_observations:
        raise InvalidArgument(
            "Calling target(%r, label=%r) would overwrite target(%r, label=%r)"
            % (observation, label, context.data.target_observations[label],
               label))
    else:
        context.data.target_observations[label] = observation

    return observation
    def run_state_machine(factory, data):
        machine = factory()
        check_type(GenericStateMachine, machine, "state_machine_factory()")
        data.conjecture_data.hypothesis_runner = machine

        n_steps = settings.stateful_step_count
        should_continue = cu.many(
            data.conjecture_data, min_size=1, max_size=n_steps, average_size=n_steps
        )

        print_steps = (
            current_build_context().is_final or current_verbosity() >= Verbosity.debug
        )
        try:
            if print_steps:
                machine.print_start()
            machine.check_invariants()

            while should_continue.more():
                value = data.conjecture_data.draw(machine.steps())
                if print_steps:
                    machine.print_step(value)
                machine.execute_step(value)
                machine.check_invariants()
        finally:
            if print_steps:
                machine.print_end()
            machine.teardown()
Beispiel #13
0
def run_state_machine_as_test(state_machine_factory, settings=None):
    """Run a state machine definition as a test, either silently doing nothing
    or printing a minimal breaking program and raising an exception.

    state_machine_factory is anything which returns an instance of
    GenericStateMachine when called with no arguments - it can be a class or a
    function. settings will be used to control the execution of the test.
    """
    if settings is None:
        try:
            settings = state_machine_factory.TestCase.settings
            check_type(Settings, settings,
                       "state_machine_factory.TestCase.settings")
        except AttributeError:
            settings = Settings(deadline=None,
                                suppress_health_check=HealthCheck.all())
    check_type(Settings, settings, "settings")

    @settings
    @given(st.data())
    def run_state_machine(factory, data):
        machine = factory()
        check_type(GenericStateMachine, machine, "state_machine_factory()")
        data.conjecture_data.hypothesis_runner = machine

        n_steps = settings.stateful_step_count
        should_continue = cu.many(data.conjecture_data,
                                  min_size=1,
                                  max_size=n_steps,
                                  average_size=n_steps)

        print_steps = (current_build_context().is_final
                       or current_verbosity() >= Verbosity.debug)
        try:
            if print_steps:
                machine.print_start()
            machine.check_invariants()

            while should_continue.more():
                value = data.conjecture_data.draw(machine.steps())
                if print_steps:
                    machine.print_step(value)
                machine.execute_step(value)
                machine.check_invariants()
        finally:
            if print_steps:
                machine.print_end()
            machine.teardown()

    # Use a machine digest to identify stateful tests in the example database
    run_state_machine.hypothesis.inner_test._hypothesis_internal_add_digest = function_digest(
        state_machine_factory)
    # Copy some attributes so @seed and @reproduce_failure "just work"
    run_state_machine._hypothesis_internal_use_seed = getattr(
        state_machine_factory, "_hypothesis_internal_use_seed", None)
    run_state_machine._hypothesis_internal_use_reproduce_failure = getattr(
        state_machine_factory, "_hypothesis_internal_use_reproduce_failure",
        None)

    run_state_machine(state_machine_factory)
Beispiel #14
0
def _max_examples_validator(x):
    check_type(int, x, name="max_examples")
    if x < 1:
        raise InvalidArgument(
            f"max_examples={x!r} should be at least one. You can disable "
            "example generation with the `phases` setting instead.")
    return x
 def get_profile(name):
     # type: (str) -> settings
     """Return the profile with the given name."""
     check_type(string_types, name, "name")
     try:
         return settings._profiles[name]
     except KeyError:
         raise InvalidArgument("Profile %r is not registered" % (name,))
Beispiel #16
0
 def get_profile(name):
     # type: (str) -> settings
     """Return the profile with the given name."""
     check_type(string_types, name, "name")
     try:
         return settings._profiles[name]
     except KeyError:
         raise InvalidArgument("Profile %r is not registered" % (name,))
Beispiel #17
0
def from_field(field):
    # type: (F) -> st.SearchStrategy[Union[F, None]]
    """Return a strategy for values that fit the given field.

    This function is used by :func:`~hypothesis.extra.django.from_form` and
    :func:`~hypothesis.extra.django.from_model` for any fields that require
    a value, or for which you passed :obj:`hypothesis.infer`.

    It's pretty similar to the core :func:`~hypothesis.strategies.from_type`
    function, with a subtle but important difference: ``from_field`` takes a
    Field *instance*, rather than a Field *subtype*, so that it has access to
    instance attributes such as string length and validators.
    """
    check_type((dm.Field, df.Field), field, "field")
    if getattr(field, "choices", False):
        choices = []  # type: list
        for value, name_or_optgroup in field.choices:
            if isinstance(name_or_optgroup, (list, tuple)):
                choices.extend(key for key, _ in name_or_optgroup)
            else:
                choices.append(value)
        # form fields automatically include an empty choice, strip it out
        if "" in choices:
            choices.remove("")
        min_size = 1
        if isinstance(field, (dm.CharField, dm.TextField)) and field.blank:
            choices.insert(0, "")
        elif isinstance(field, (df.Field)) and not field.required:
            choices.insert(0, "")
            min_size = 0
        strategy = st.sampled_from(choices)
        if isinstance(field,
                      (df.MultipleChoiceField, df.TypedMultipleChoiceField)):
            strategy = st.lists(st.sampled_from(choices), min_size=min_size)
    else:
        if type(field) not in _global_field_lookup:
            if getattr(field, "null", False):
                return st.none()
            raise InvalidArgument("Could not infer a strategy for %r",
                                  (field, ))
        strategy = _global_field_lookup[type(field)]  # type: ignore
        if not isinstance(strategy, st.SearchStrategy):
            strategy = strategy(field)
    assert isinstance(strategy, st.SearchStrategy)
    if field.validators:

        def validate(value):
            try:
                field.run_validators(value)
                return True
            except django.core.exceptions.ValidationError:
                return False

        strategy = strategy.filter(validate)

    if getattr(field, "null", False):
        return st.none() | strategy
    return strategy
Beispiel #18
0
def target(observation, label=""):
    # type: (float, str) -> None
    """Calling this function with a ``float`` observation gives it feedback
    with which to guide our search for inputs that will cause an error, in
    addition to all the usual heuristics.  Observations must always be finite.

    Hypothesis will try to maximize the observed value over several examples;
    almost any metric will work so long as it makes sense to increase it.
    For example, ``-abs(error)`` is a metric that increases as ``error``
    approaches zero.

    Example metrics:

    - Number of elements in a collection, or tasks in a queue
    - Mean or maximum runtime of a task (or both, if you use ``label``)
    - Compression ratio for data (perhaps per-algorithm or per-level)
    - Number of steps taken by a state machine

    The optional ``label`` argument can be used to distinguish between
    and therefore separately optimise distinct observations, such as the
    mean and standard deviation of a dataset.  It is an error to call
    ``target()`` with any label more than once per test case.

    .. note::
        **The more examples you run, the better this technique works.**

        As a rule of thumb, the targeting effect is noticeable above
        :obj:`max_exmples=1000 <hypothesis.settings.max_examples>`,
        and immediately obvious by around ten thousand examples
        *per label* used by your test.

    .. note::
        ``hypothesis.target`` is considered experimental, and may be radically
        changed or even removed in a future version.  If you find it useful,
        please let us know so we can share and build on that success!
    """
    check_type(float, observation, "observation")
    if math.isinf(observation) or math.isnan(observation):
        raise InvalidArgument("observation=%r must be a finite float." %
                              observation)
    check_type(string_types, label, "label")

    context = _current_build_context.value
    if context is None:
        raise InvalidArgument("Calling target() outside of a test is invalid.")
    verbose_report("Saw target(observation=%r, label=%r)" %
                   (observation, label))

    if context.data is not None:
        if label in context.data.target_observations:
            raise InvalidArgument(
                "Calling target(%r, label=%r) would overwrite target(%r, label=%r)"
                % (observation, label, context.data.target_observations[label],
                   label))
        else:
            context.data.target_observations[label] = observation
Beispiel #19
0
def _validate_bindings(draw, elements, name=""):
	"""DOCUMENT ME!!!"""

	# Use OrderedDict over regular to enforce strictness of binding position
	# because they tranlate directly to a linear memory map.
	check_type(OrderedDict, elements, name)

	# preallocated memory pool for optimized access times.
	def static(): return tuple(None for elm in elements)

	bindings = static()
	values = static()
	unknown = []

	# This is some really funky syntax python (*-*) enumerate my soul
	for pos, (key, value) in enumerate(elements.items()):
		# Don't forget to make sure our children's values are strategies
		# before we waste any resources on generating and ordering them.
		check_strategy(value, name="value at key '%s' in %s" % (key, name))

		if isinstance(key, int):
			unknown.append(pos)
		elif isinstance(key, text_type):
			if not _supported_binding_regex.match(key):
				try:
					key = draw(hs.from_regex(key))
				except(sre_constants.error):
					raise InvalidArgument(
						"Could not satisfy requirements of binding \
						'%s' at index '%i' in %s, invalid regex."
						% (key, index, name)
					)

			bindings[pos] = key
		else:
			raise InvalidArgument(
				"Expected binding at index '%d' of '%s' be int or %s, \
				but got '%r' (type=%s)"
				% (pos, name, text_type, key, type(key).__name__)
			)

		# Generate our children after sorting; micro optimization
		values[pos] = draw(value)

	unknown_bindings = len(lost_members)
	generated_bindings = draw(hs.lists(
		hs.from_regex(_supported_binding_regex),
		min_size=unknown_bindings,
		max_size=unknown_bindings,
		unique=True,
	))

	for long, lat in enumerate(unknown_bindings):
		bindings[lat] = generated_bindings[long]

	return (bindings, values)
Beispiel #20
0
    def load_profile(name: str) -> None:
        """Loads in the settings defined in the profile provided.

        If the profile does not exist, InvalidArgument will be raised.
        Any setting not defined in the profile will be the library
        defined default for that setting.
        """
        check_type(str, name, "name")
        settings._current_profile = name
        settings._assign_default_internal(settings.get_profile(name))
Beispiel #21
0
def from_dtype(dtype):
    # type: (np.dtype) -> st.SearchStrategy[Any]
    """Creates a strategy which can generate any value of the given dtype."""
    check_type(np.dtype, dtype, "dtype")
    # Compound datatypes, eg 'f4,f4,f4'
    if dtype.names is not None:
        # mapping np.void.type over a strategy is nonsense, so return now.
        return st.tuples(*[from_dtype(dtype.fields[name][0]) for name in dtype.names])

    # Subarray datatypes, eg '(2, 3)i4'
    if dtype.subdtype is not None:
        subtype, shape = dtype.subdtype
        return arrays(subtype, shape)

    # Scalar datatypes
    if dtype.kind == u"b":
        result = st.booleans()  # type: SearchStrategy[Any]
    elif dtype.kind == u"f":
        if dtype.itemsize == 2:
            result = st.floats(width=16)
        elif dtype.itemsize == 4:
            result = st.floats(width=32)
        else:
            result = st.floats()
    elif dtype.kind == u"c":
        if dtype.itemsize == 8:
            float32 = st.floats(width=32)
            result = st.builds(complex, float32, float32)
        else:
            result = st.complex_numbers()
    elif dtype.kind in (u"S", u"a"):
        # Numpy strings are null-terminated; only allow round-trippable values.
        # `itemsize == 0` means 'fixed length determined at array creation'
        result = st.binary(max_size=dtype.itemsize or None).filter(
            lambda b: b[-1:] != b"\0"
        )
    elif dtype.kind == u"u":
        result = st.integers(min_value=0, max_value=2 ** (8 * dtype.itemsize) - 1)
    elif dtype.kind == u"i":
        overflow = 2 ** (8 * dtype.itemsize - 1)
        result = st.integers(min_value=-overflow, max_value=overflow - 1)
    elif dtype.kind == u"U":
        # Encoded in UTF-32 (four bytes/codepoint) and null-terminated
        result = st.text(max_size=(dtype.itemsize or 0) // 4 or None).filter(
            lambda b: b[-1:] != u"\0"
        )
    elif dtype.kind in (u"m", u"M"):
        if "[" in dtype.str:
            res = st.just(dtype.str.split("[")[-1][:-1])
        else:
            res = st.sampled_from(TIME_RESOLUTIONS)
        result = st.builds(dtype.type, st.integers(-(2 ** 63), 2 ** 63 - 1), res)
    else:
        raise InvalidArgument(u"No strategy inference for {}".format(dtype))
    return result.map(dtype.type)
Beispiel #22
0
def from_dtype(dtype):
    # type: (np.dtype) -> st.SearchStrategy[Any]
    """Creates a strategy which can generate any value of the given dtype."""
    check_type(np.dtype, dtype, 'dtype')
    # Compound datatypes, eg 'f4,f4,f4'
    if dtype.names is not None:
        # mapping np.void.type over a strategy is nonsense, so return now.
        return st.tuples(
            *[from_dtype(dtype.fields[name][0]) for name in dtype.names])

    # Subarray datatypes, eg '(2, 3)i4'
    if dtype.subdtype is not None:
        subtype, shape = dtype.subdtype
        return arrays(subtype, shape)

    # Scalar datatypes
    if dtype.kind == u'b':
        result = st.booleans()  # type: SearchStrategy[Any]
    elif dtype.kind == u'f':
        if dtype.itemsize == 2:
            result = st.floats(width=16)
        elif dtype.itemsize == 4:
            result = st.floats(width=32)
        else:
            result = st.floats()
    elif dtype.kind == u'c':
        if dtype.itemsize == 8:
            float32 = st.floats(width=32)
            result = st.builds(complex, float32, float32)
        else:
            result = st.complex_numbers()
    elif dtype.kind in (u'S', u'a'):
        # Numpy strings are null-terminated; only allow round-trippable values.
        # `itemsize == 0` means 'fixed length determined at array creation'
        result = st.binary(max_size=dtype.itemsize or None
                           ).filter(lambda b: b[-1:] != b'\0')
    elif dtype.kind == u'u':
        result = st.integers(min_value=0,
                             max_value=2 ** (8 * dtype.itemsize) - 1)
    elif dtype.kind == u'i':
        overflow = 2 ** (8 * dtype.itemsize - 1)
        result = st.integers(min_value=-overflow, max_value=overflow - 1)
    elif dtype.kind == u'U':
        # Encoded in UTF-32 (four bytes/codepoint) and null-terminated
        result = st.text(max_size=(dtype.itemsize or 0) // 4 or None
                         ).filter(lambda b: b[-1:] != u'\0')
    elif dtype.kind in (u'm', u'M'):
        if '[' in dtype.str:
            res = st.just(dtype.str.split('[')[-1][:-1])
        else:
            res = st.sampled_from(TIME_RESOLUTIONS)
        result = st.builds(dtype.type, st.integers(-2**63, 2**63 - 1), res)
    else:
        raise InvalidArgument(u'No strategy inference for {}'.format(dtype))
    return result.map(dtype.type)
    def load_profile(name):
        # type: (str) -> None
        """Loads in the settings defined in the profile provided.

        If the profile does not exist, InvalidArgument will be raised.
        Any setting not defined in the profile will be the library
        defined default for that setting.
        """
        check_type(string_types, name, "name")
        settings._current_profile = name
        settings._assign_default_internal(settings.get_profile(name))
def from_field(field):
    # type: (Type[dm.Field]) -> st.SearchStrategy[dm.Field]
    """Return a strategy for values that fit the given field.

    This is pretty similar to the core `from_type` function, with a subtle
    but important difference: `from_field` takes a Field *instance*, rather
    than a Field *subtype*, so that it has access to instance attributes
    such as string length and validators.
    """
    check_type((dm.Field, df.Field), field, "field")
    if getattr(field, "choices", False):
        choices = []  # type: list
        for value, name_or_optgroup in field.choices:
            if isinstance(name_or_optgroup, (list, tuple)):
                choices.extend(key for key, _ in name_or_optgroup)
            else:
                choices.append(value)
        # form fields automatically include an empty choice, strip it out
        if u"" in choices:
            choices.remove(u"")
        min_size = 1
        if isinstance(field, (dm.CharField, dm.TextField)) and field.blank:
            choices.insert(0, u"")
        elif isinstance(field, (df.Field)) and not field.required:
            choices.insert(0, u"")
            min_size = 0
        strategy = st.sampled_from(choices)
        if isinstance(field, (df.MultipleChoiceField, df.TypedMultipleChoiceField)):
            strategy = st.lists(st.sampled_from(choices), min_size=min_size)
    else:
        if type(field) not in _global_field_lookup:
            if getattr(field, "null", False):
                return st.none()
            raise InvalidArgument("Could not infer a strategy for %r", (field,))
        strategy = _global_field_lookup[type(field)]
        if not isinstance(strategy, st.SearchStrategy):
            strategy = strategy(field)
    assert isinstance(strategy, st.SearchStrategy)
    if field.validators:

        def validate(value):
            try:
                field.run_validators(value)
                return True
            except django.core.exceptions.ValidationError:
                return False

        strategy = strategy.filter(validate)

    if getattr(field, "null", False):
        return st.none() | strategy
    return strategy
Beispiel #25
0
def invariant(*,
              check_during_init: bool = False
              ) -> Callable[[TestFunc], TestFunc]:
    """Decorator to apply an invariant for rules in a RuleBasedStateMachine.
    The decorated function will be run after every rule and can raise an
    exception to indicate failed invariants.

    For example::

        class MyTestMachine(RuleBasedStateMachine):
            state = 1

            @invariant()
            def is_nonzero(self):
                assert self.state != 0

    By default, invariants are only checked after all
    :func:`@initialize() <hypothesis.stateful.initialize>` rules have been run.
    Pass ``check_during_init=True`` for invariants which can also be checked
    during initialization.
    """
    check_type(bool, check_during_init, "check_during_init")

    def accept(f):
        if getattr(f, RULE_MARKER, None) or getattr(f, INITIALIZE_RULE_MARKER,
                                                    None):
            raise InvalidDefinition(
                "A function cannot be used for both a rule and an invariant.",
                Settings.default,
            )
        existing_invariant = getattr(f, INVARIANT_MARKER, None)
        if existing_invariant is not None:
            raise InvalidDefinition(
                "A function cannot be used for two distinct invariants.",
                Settings.default,
            )
        preconditions = getattr(f, PRECONDITIONS_MARKER, ())
        invar = Invariant(
            function=f,
            preconditions=preconditions,
            check_during_init=check_during_init,
        )

        @proxies(f)
        def invariant_wrapper(*args, **kwargs):
            return f(*args, **kwargs)

        setattr(invariant_wrapper, INVARIANT_MARKER, invar)
        return invariant_wrapper

    return accept
Beispiel #26
0
def array_shapes(min_dims=1, max_dims=None, min_side=1, max_side=None):
    # type: (int, int, int, int) -> st.SearchStrategy[Shape]
    """Return a strategy for array shapes (tuples of int >= 1)."""
    check_type(integer_types, min_dims, "min_dims")
    check_type(integer_types, min_side, "min_side")
    if min_dims > 32:
        raise InvalidArgument(
            "Got min_dims=%r, but numpy does not support arrays greater than 32 dimensions"
            % min_dims
        )
    if max_dims is None:
        max_dims = min(min_dims + 2, 32)
    check_type(integer_types, max_dims, "max_dims")
    if max_dims > 32:
        raise InvalidArgument(
            "Got max_dims=%r, but numpy does not support arrays greater than 32 dimensions"
            % max_dims
        )
    if max_side is None:
        max_side = min_side + 5
    check_type(integer_types, max_side, "max_side")
    order_check("dims", 0, min_dims, max_dims)
    order_check("side", 0, min_side, max_side)

    return st.lists(
        st.integers(min_side, max_side), min_size=min_dims, max_size=max_dims
    ).map(tuple)
Beispiel #27
0
def array_shapes(
    *,
    min_dims: int = 1,
    max_dims: Optional[int] = None,
    min_side: int = 1,
    max_side: Optional[int] = None,
) -> st.SearchStrategy[Shape]:
    """Return a strategy for array shapes (tuples of int >= 1).

    * ``min_dims`` is the smallest length that the generated shape can possess.
    * ``max_dims`` is the largest length that the generated shape can possess,
      defaulting to ``min_dims + 2``.
    * ``min_side`` is the smallest size that a dimension can possess.
    * ``max_side`` is the largest size that a dimension can possess,
      defaulting to ``min_side + 5``.
    """
    check_type(int, min_dims, "min_dims")
    check_type(int, min_side, "min_side")
    check_valid_dims(min_dims, "min_dims")

    if max_dims is None:
        max_dims = min(min_dims + 2, NDIM_MAX)
    check_type(int, max_dims, "max_dims")
    check_valid_dims(max_dims, "max_dims")

    if max_side is None:
        max_side = min_side + 5
    check_type(int, max_side, "max_side")

    order_check("dims", 0, min_dims, max_dims)
    order_check("side", 0, min_side, max_side)

    return st.lists(
        st.integers(min_side, max_side), min_size=min_dims, max_size=max_dims
    ).map(tuple)
Beispiel #28
0
def indices(
    shape: Shape,
    *,
    min_dims: int = 0,
    max_dims: Optional[int] = None,
    allow_ellipsis: bool = True,
) -> st.SearchStrategy[BasicIndex]:
    """Return a strategy for :xp-ref:`valid indices <indexing.html>` of
    arrays with the specified shape, which may include dimensions of size zero.

    It generates tuples containing some mix of integers, :obj:`python:slice`
    objects, and ``...`` (an ``Ellipsis``). When a length-one tuple would be
    generated, this strategy may instead return the element which will index the
    first axis, e.g. ``5`` instead of ``(5,)``.

    * ``shape`` is the shape of the array that will be indexed, as a tuple of
      integers >= 0. This must be at least two-dimensional for a tuple to be a
      valid index;  for one-dimensional arrays use
      :func:`~hypothesis.strategies.slices` instead.
    * ``min_dims`` is the minimum dimensionality of the resulting array from use
      of the generated index.
    * ``max_dims`` is the the maximum dimensionality of the resulting array,
      defaulting to ``len(shape)``.
    * ``allow_ellipsis`` specifies whether ``...`` is allowed in the index.
    """
    check_type(tuple, shape, "shape")
    check_argument(
        all(isinstance(x, int) and x >= 0 for x in shape),
        f"shape={shape!r}, but all dimensions must be non-negative integers.",
    )
    check_type(bool, allow_ellipsis, "allow_ellipsis")
    check_type(int, min_dims, "min_dims")
    check_argument(
        min_dims <= len(shape),
        f"min_dims={min_dims} is larger than len(shape)={len(shape)}, "
        "but it is impossible for an indexing operation to add dimensions.",
    )
    check_valid_dims(min_dims, "min_dims")

    if max_dims is None:
        max_dims = min(len(shape), NDIM_MAX)
    check_type(int, max_dims, "max_dims")
    assert isinstance(max_dims, int)
    check_argument(
        max_dims <= len(shape),
        f"max_dims={max_dims} is larger than len(shape)={len(shape)}, "
        "but it is impossible for an indexing operation to add dimensions.",
    )
    check_valid_dims(max_dims, "max_dims")

    order_check("dims", 0, min_dims, max_dims)

    return BasicIndexStrategy(
        shape,
        min_dims=min_dims,
        max_dims=max_dims,
        allow_ellipsis=allow_ellipsis,
        allow_newaxis=False,
        allow_fewer_indices_than_dims=False,
    )
Beispiel #29
0
def dates(min_value: dt.date = dt.date.min,
          max_value: dt.date = dt.date.max) -> SearchStrategy[dt.date]:
    """dates(min_value=datetime.date.min, max_value=datetime.date.max)

    A strategy for dates between ``min_value`` and ``max_value``.

    Examples from this strategy shrink towards January 1st 2000.
    """
    check_type(dt.date, min_value, "min_value")
    check_type(dt.date, max_value, "max_value")
    check_valid_interval(min_value, max_value, "min_value", "max_value")
    if min_value == max_value:
        return just(min_value)
    return DateStrategy(min_value, max_value)
Beispiel #30
0
    def __init__(self, grammar, start=None):
        check_type(lark.lark.Lark, grammar, "grammar")
        if start is None:
            start = grammar.options.start
        if not isinstance(start, list):
            start = [start]
        self.grammar = grammar

        if "start" in getfullargspec(grammar.grammar.compile).args:
            terminals, rules, ignore_names = grammar.grammar.compile(start)
        else:  # pragma: no cover
            # This branch is to support lark <= 0.7.1, without the start argument.
            terminals, rules, ignore_names = grammar.grammar.compile()

        self.names_to_symbols = {}

        for r in rules:
            t = r.origin
            self.names_to_symbols[t.name] = t

        for t in terminals:
            self.names_to_symbols[t.name] = Terminal(t.name)

        self.start = st.sampled_from([self.names_to_symbols[s] for s in start])

        self.ignored_symbols = (st.sampled_from(
            [self.names_to_symbols[n]
             for n in ignore_names]) if ignore_names else st.nothing())

        self.terminal_strategies = {
            t.name: st.from_regex(t.pattern.to_regexp(), fullmatch=True)
            for t in terminals
        }

        nonterminals = {}

        for rule in rules:
            nonterminals.setdefault(rule.origin.name,
                                    []).append(tuple(rule.expansion))

        for v in nonterminals.values():
            v.sort(key=len)

        self.nonterminal_strategies = {
            k: st.sampled_from(v)
            for k, v in nonterminals.items()
        }

        self.__rule_labels = {}
Beispiel #31
0
def ip_addresses(
    *,
    v: int = None,
    network: Union[str, IPv4Network, IPv6Network] = None
) -> SearchStrategy[Union[IPv4Address, IPv6Address]]:
    r"""Generate IP addresses - ``v=4`` for :class:`~python:ipaddress.IPv4Address`\ es,
    ``v=6`` for :class:`~python:ipaddress.IPv6Address`\ es, or leave unspecified
    to allow both versions.

    ``network`` may be an :class:`~python:ipaddress.IPv4Network` or
    :class:`~python:ipaddress.IPv6Network`, or a string representing a network such as
    ``"127.0.0.0/24"`` or ``"2001:db8::/32"``.  As well as generating addresses within
    a particular routable network, this can be used to generate addresses from a
    reserved range listed in the
    `IANA <https://www.iana.org/assignments/iana-ipv4-special-registry/>`__
    `registries <https://www.iana.org/assignments/iana-ipv6-special-registry/>`__.

    If you pass both ``v`` and ``network``, they must be for the same version.
    """
    if v is not None:
        check_type(int, v, "v")
        if v != 4 and v != 6:
            raise InvalidArgument("v=%r, but only v=4 or v=6 are valid" %
                                  (v, ))
    if network is None:
        # We use the reserved-address registries to boost the chance
        # of generating one of the various special types of address.
        four = binary(
            4, 4).map(IPv4Address) | sampled_from(SPECIAL_IPv4_RANGES).flatmap(
                lambda network: ip_addresses(network=network))
        six = binary(
            16,
            16).map(IPv6Address) | sampled_from(SPECIAL_IPv6_RANGES).flatmap(
                lambda network: ip_addresses(network=network))
        if v == 4:
            return four
        if v == 6:
            return six
        return four | six
    if isinstance(network, str):
        network = ip_network(network)
    check_type((IPv4Network, IPv6Network), network, "network")
    assert isinstance(network, (IPv4Network, IPv6Network))  # for Mypy
    if v not in (None, network.version):
        raise InvalidArgument("v=%r is incompatible with network=%r" %
                              (v, network))
    addr_type = IPv4Address if network.version == 4 else IPv6Address
    return integers(int(network[0]),
                    int(network[-1])).map(addr_type)  # type: ignore
Beispiel #32
0
def _validate_print_blob(value):
    if isinstance(value, PrintSettings):
        replacement = value != PrintSettings.NEVER

        note_deprecation(
            "Setting print_blob=%r is deprecated and will become an error "
            "in a future version of Hypothesis. Use print_blob=%r instead."
            % (value, replacement),
            since="2018-09-30",
        )
        return replacement

    check_type(bool, value, "print_blob")

    return value
Beispiel #33
0
    def register_profile(name: str, parent: "settings" = None, **kwargs: Any) -> None:
        """Registers a collection of values to be used as a settings profile.

        Settings profiles can be loaded by name - for example, you might
        create a 'fast' profile which runs fewer examples, keep the 'default'
        profile, and create a 'ci' profile that increases the number of
        examples and uses a different database to store failures.

        The arguments to this method are exactly as for
        :class:`~hypothesis.settings`: optional ``parent`` settings, and
        keyword arguments for each setting that will be set differently to
        parent (or settings.default, if parent is None).
        """
        check_type(str, name, "name")
        settings._profiles[name] = settings(parent=parent, **kwargs)
Beispiel #34
0
def timedeltas(
    min_value: dt.timedelta = dt.timedelta.min,
    max_value: dt.timedelta = dt.timedelta.max,
) -> SearchStrategy[dt.timedelta]:
    """timedeltas(min_value=datetime.timedelta.min, max_value=datetime.timedelta.max)

    A strategy for timedeltas between ``min_value`` and ``max_value``.

    Examples from this strategy shrink towards zero.
    """
    check_type(dt.timedelta, min_value, "min_value")
    check_type(dt.timedelta, max_value, "max_value")
    check_valid_interval(min_value, max_value, "min_value", "max_value")
    if min_value == max_value:
        return just(min_value)
    return TimedeltaStrategy(min_value=min_value, max_value=max_value)
    def register_profile(name, parent=None, **kwargs):
        # type: (str, settings, **Any) -> None
        """Registers a collection of values to be used as a settings profile.

        Settings profiles can be loaded by name - for example, you might
        create a 'fast' profile which runs fewer examples, keep the 'default'
        profile, and create a 'ci' profile that increases the number of
        examples and uses a different database to store failures.

        The arguments to this method are exactly as for
        :class:`~hypothesis.settings`: optional ``parent`` settings, and
        keyword arguments for each setting that will be set differently to
        parent (or settings.default, if parent is None).
        """
        check_type(string_types, name, "name")
        settings._profiles[name] = settings(parent=parent, **kwargs)
Beispiel #36
0
def rule(targets=(), target=None, **kwargs):
    """Decorator for RuleBasedStateMachine. Any name present in target or
    targets will define where the end result of this function should go. If
    both are empty then the end result will be discarded.

    ``target`` must be a Bundle, or if the result should go to multiple
    bundles you can pass a tuple of them as the ``targets`` argument.
    It is invalid to use both arguments for a single rule.  If the result
    should go to exactly one of several bundles, define a separate rule for
    each case.

    kwargs then define the arguments that will be passed to the function
    invocation. If their value is a Bundle, or if it is ``consumes(b)``
    where ``b`` is a Bundle, then values that have previously been produced
    for that bundle will be provided. If ``consumes`` is used, the value
    will also be removed from the bundle.

    Any other kwargs should be strategies and values from them will be
    provided.
    """
    converted_targets = _convert_targets(targets, target)
    for k, v in kwargs.items():
        check_type(SearchStrategy, v, k)

    def accept(f):
        existing_rule = getattr(f, RULE_MARKER, None)
        existing_initialize_rule = getattr(f, INITIALIZE_RULE_MARKER, None)
        if existing_rule is not None or existing_initialize_rule is not None:
            raise InvalidDefinition(
                "A function cannot be used for two distinct rules. ",
                Settings.default)
        precondition = getattr(f, PRECONDITION_MARKER, None)
        rule = Rule(
            targets=converted_targets,
            arguments=kwargs,
            function=f,
            precondition=precondition,
        )

        @proxies(f)
        def rule_wrapper(*args, **kwargs):
            return f(*args, **kwargs)

        setattr(rule_wrapper, RULE_MARKER, rule)
        return rule_wrapper

    return accept
Beispiel #37
0
    def run_state_machine(factory, data):
        machine = factory()
        if isinstance(machine, GenericStateMachine) and not isinstance(
                machine, RuleBasedStateMachine):
            note_deprecation(
                "%s inherits from GenericStateMachine, which is deprecated.  Use a "
                "RuleBasedStateMachine, or a test function with st.data(), instead."
                % (type(machine).__name__, ),
                since="2019-05-29",
            )
        else:
            check_type(RuleBasedStateMachine, machine,
                       "state_machine_factory()")
        data.conjecture_data.hypothesis_runner = machine

        n_steps = settings.stateful_step_count
        should_continue = cu.many(data.conjecture_data,
                                  min_size=1,
                                  max_size=n_steps,
                                  average_size=n_steps)

        print_steps = (current_build_context().is_final
                       or current_verbosity() >= Verbosity.debug)
        try:
            if print_steps:
                machine.print_start()
            machine.check_invariants()

            while should_continue.more():
                value = data.conjecture_data.draw(machine.steps())
                # Assign 'result' here in case 'execute_step' fails below
                result = multiple()
                try:
                    result = machine.execute_step(value)
                finally:
                    if print_steps:
                        # 'result' is only used if the step has target bundles.
                        # If it does, and the result is a 'MultipleResult',
                        # then 'print_step' prints a multi-variable assignment.
                        machine.print_step(value, result)
                machine.check_invariants()
        finally:
            if print_steps:
                machine.print_end()
            machine.teardown()
    def __init__(self, grammar, start=None):
        check_type(lark.lark.Lark, grammar, "grammar")
        if start is None:
            start = grammar.options.start
        self.grammar = grammar

        terminals, rules, ignore_names = grammar.grammar.compile()

        self.names_to_symbols = {}

        for r in rules:
            t = r.origin
            self.names_to_symbols[t.name] = t

        for t in terminals:
            self.names_to_symbols[t.name] = Terminal(t.name)

        self.start = self.names_to_symbols[start]

        self.ignored_symbols = (
            st.sampled_from([self.names_to_symbols[n] for n in ignore_names])
            if ignore_names
            else st.nothing()
        )

        self.terminal_strategies = {
            t.name: st.from_regex(t.pattern.to_regexp(), fullmatch=True)
            for t in terminals
        }

        nonterminals = {}

        for rule in rules:
            nonterminals.setdefault(rule.origin.name, []).append(tuple(rule.expansion))

        for v in nonterminals.values():
            v.sort(key=len)

        self.nonterminal_strategies = {
            k: st.sampled_from(v) for k, v in nonterminals.items()
        }

        self.__rule_labels = {}
def register_field_strategy(field_type, strategy):
    # type: (Type[dm.Field], st.SearchStrategy) -> None
    """Add an entry to the global field-to-strategy lookup used by from_field.

    ``field_type`` must be a subtype of django.db.models.Field, which must not
    already be registered.  ``strategy`` must be a SearchStrategy.
    """
    if not issubclass(field_type, (dm.Field, df.Field)):
        raise InvalidArgument(
            "field_type=%r must be a subtype of Field" % (field_type,)
        )
    check_type(st.SearchStrategy, strategy, "strategy")
    if field_type in _global_field_lookup:
        raise InvalidArgument(
            "field_type=%r already has a registered strategy (%r)"
            % (field_type, _global_field_lookup[field_type])
        )
    if issubclass(field_type, dm.AutoField):
        raise InvalidArgument("Cannot register a strategy for an AutoField")
    _global_field_lookup[field_type] = strategy
def indexes(
    elements=None,  # type: st.SearchStrategy[Ex]
    dtype=None,  # type: Any
    min_size=0,  # type: int
    max_size=None,  # type: int
    unique=True,  # type: bool
):
    """Provides a strategy for producing a :class:`pandas.Index`.

    Arguments:

    * elements is a strategy which will be used to generate the individual
      values of the index. If None, it will be inferred from the dtype. Note:
      even if the elements strategy produces tuples, the generated value
      will not be a MultiIndex, but instead be a normal index whose elements
      are tuples.
    * dtype is the dtype of the resulting index. If None, it will be inferred
      from the elements strategy. At least one of dtype or elements must be
      provided.
    * min_size is the minimum number of elements in the index.
    * max_size is the maximum number of elements in the index. If None then it
      will default to a suitable small size. If you want larger indexes you
      should pass a max_size explicitly.
    * unique specifies whether all of the elements in the resulting index
      should be distinct.
    """
    check_valid_size(min_size, "min_size")
    check_valid_size(max_size, "max_size")
    check_valid_interval(min_size, max_size, "min_size", "max_size")
    check_type(bool, unique, "unique")

    elements, dtype = elements_and_dtype(elements, dtype)

    if max_size is None:
        max_size = min_size + DEFAULT_MAX_SIZE
    return ValueIndexStrategy(elements, dtype, min_size, max_size, unique)
 def do_draw(self, data):
     source = data.draw(self.flatmapped_strategy)
     expanded_source = self.expand(source)
     check_type(SearchStrategy, expanded_source)
     return data.draw(expanded_source)
Beispiel #42
0
def check_strategy(arg, name=''):
    check_type(SearchStrategy, arg, name)
def test_check_type_works_for_old_style_classes():
    check_type(OldStyleClass, OldStyleClass())
    with pytest.raises(InvalidArgument):
        check_type(OldStyleClass, "not an instance")
 def row():
     result = draw(rows)
     check_type(Iterable, result, "draw(row)")
     return result
def data_frames(
    columns=None,  # type: Sequence[column]
    rows=None,  # type: st.SearchStrategy[Union[dict, Sequence[Any]]]
    index=None,  # type: st.SearchStrategy[Ex]
):
    # type: (...) -> st.SearchStrategy[pandas.DataFrame]
    """Provides a strategy for producing a :class:`pandas.DataFrame`.

    Arguments:

    * columns: An iterable of :class:`column` objects describing the shape
      of the generated DataFrame.

    * rows: A strategy for generating a row object. Should generate
      either dicts mapping column names to values or a sequence mapping
      column position to the value in that position (note that unlike the
      :class:`pandas.DataFrame` constructor, single values are not allowed
      here. Passing e.g. an integer is an error, even if there is only one
      column).

      At least one of rows and columns must be provided. If both are
      provided then the generated rows will be validated against the
      columns and an error will be raised if they don't match.

      Caveats on using rows:

      * In general you should prefer using columns to rows, and only use
        rows if the columns interface is insufficiently flexible to
        describe what you need - you will get better performance and
        example quality that way.
      * If you provide rows and not columns, then the shape and dtype of
        the resulting DataFrame may vary. e.g. if you have a mix of int
        and float in the values for one column in your row entries, the
        column will sometimes have an integral dtype and sometimes a float.

    * index: If not None, a strategy for generating indexes for the
      resulting DataFrame. This can generate either :class:`pandas.Index`
      objects or any sequence of values (which will be passed to the
      Index constructor).

      You will probably find it most convenient to use the
      :func:`~hypothesis.extra.pandas.indexes` or
      :func:`~hypothesis.extra.pandas.range_indexes` function to produce
      values for this argument.

    Usage:

    The expected usage pattern is that you use :class:`column` and
    :func:`columns` to specify a fixed shape of the DataFrame you want as
    follows. For example the following gives a two column data frame:

    .. code-block:: pycon

        >>> from hypothesis.extra.pandas import column, data_frames
        >>> data_frames([
        ... column('A', dtype=int), column('B', dtype=float)]).example()
                    A              B
        0  2021915903  1.793898e+232
        1  1146643993            inf
        2 -2096165693   1.000000e+07

    If you want the values in different columns to interact in some way you
    can use the rows argument. For example the following gives a two column
    DataFrame where the value in the first column is always at most the value
    in the second:

    .. code-block:: pycon

        >>> from hypothesis.extra.pandas import column, data_frames
        >>> import hypothesis.strategies as st
        >>> data_frames(
        ...     rows=st.tuples(st.floats(allow_nan=False),
        ...                    st.floats(allow_nan=False)).map(sorted)
        ... ).example()
                       0             1
        0  -3.402823e+38  9.007199e+15
        1 -1.562796e-298  5.000000e-01

    You can also combine the two:

    .. code-block:: pycon

        >>> from hypothesis.extra.pandas import columns, data_frames
        >>> import hypothesis.strategies as st
        >>> data_frames(
        ...     columns=columns(["lo", "hi"], dtype=float),
        ...     rows=st.tuples(st.floats(allow_nan=False),
        ...                    st.floats(allow_nan=False)).map(sorted)
        ... ).example()
                 lo            hi
        0   9.314723e-49  4.353037e+45
        1  -9.999900e-01  1.000000e+07
        2 -2.152861e+134 -1.069317e-73

    (Note that the column dtype must still be specified and will not be
    inferred from the rows. This restriction may be lifted in future).

    Combining rows and columns has the following behaviour:

    * The column names and dtypes will be used.
    * If the column is required to be unique, this will be enforced.
    * Any values missing from the generated rows will be provided using the
      column's fill.
    * Any values in the row not present in the column specification (if
      dicts are passed, if there are keys with no corresponding column name,
      if sequences are passed if there are too many items) will result in
      InvalidArgument being raised.
    """
    if index is None:
        index = range_indexes()
    else:
        st.check_strategy(index)

    index_strategy = index

    if columns is None:
        if rows is None:
            raise InvalidArgument("At least one of rows and columns must be provided")
        else:

            @st.composite
            def rows_only(draw):
                index = draw(index_strategy)

                @check_function
                def row():
                    result = draw(rows)
                    check_type(Iterable, result, "draw(row)")
                    return result

                if len(index) > 0:
                    return pandas.DataFrame([row() for _ in index], index=index)
                else:
                    # If we haven't drawn any rows we need to draw one row and
                    # then discard it so that we get a consistent shape for the
                    # DataFrame.
                    base = pandas.DataFrame([row()])
                    return base.drop(0)

            return rows_only()

    assert columns is not None
    cols = try_convert(tuple, columns, "columns")  # type: Sequence[column]

    rewritten_columns = []
    column_names = set()  # type: Set[str]

    for i, c in enumerate(cols):
        check_type(column, c, "columns[%d]" % (i,))

        c = copy(c)
        if c.name is None:
            label = "columns[%d]" % (i,)
            c.name = i
        else:
            label = c.name
            try:
                hash(c.name)
            except TypeError:
                raise InvalidArgument(
                    "Column names must be hashable, but columns[%d].name was "
                    "%r of type %s, which cannot be hashed."
                    % (i, c.name, type(c.name).__name__)
                )

        if c.name in column_names:
            raise InvalidArgument("duplicate definition of column name %r" % (c.name,))

        column_names.add(c.name)

        c.elements, c.dtype = elements_and_dtype(c.elements, c.dtype, label)

        if c.dtype is None and rows is not None:
            raise InvalidArgument(
                "Must specify a dtype for all columns when combining rows with"
                " columns."
            )

        c.fill = npst.fill_for(
            fill=c.fill, elements=c.elements, unique=c.unique, name=label
        )

        rewritten_columns.append(c)

    if rows is None:

        @st.composite
        def just_draw_columns(draw):
            index = draw(index_strategy)
            local_index_strategy = st.just(index)

            data = OrderedDict((c.name, None) for c in rewritten_columns)

            # Depending on how the columns are going to be generated we group
            # them differently to get better shrinking. For columns with fill
            # enabled, the elements can be shrunk independently of the size,
            # so we can just shrink by shrinking the index then shrinking the
            # length and are generally much more free to move data around.

            # For columns with no filling the problem is harder, and drawing
            # them like that would result in rows being very far apart from
            # each other in the underlying data stream, which gets in the way
            # of shrinking. So what we do is reorder and draw those columns
            # row wise, so that the values of each row are next to each other.
            # This makes life easier for the shrinker when deleting blocks of
            # data.
            columns_without_fill = [c for c in rewritten_columns if c.fill.is_empty]

            if columns_without_fill:
                for c in columns_without_fill:
                    data[c.name] = pandas.Series(
                        np.zeros(shape=len(index), dtype=c.dtype), index=index
                    )
                seen = {c.name: set() for c in columns_without_fill if c.unique}

                for i in hrange(len(index)):
                    for c in columns_without_fill:
                        if c.unique:
                            for _ in range(5):
                                value = draw(c.elements)
                                if value not in seen[c.name]:
                                    seen[c.name].add(value)
                                    break
                            else:
                                reject()
                        else:
                            value = draw(c.elements)
                        data[c.name][i] = value

            for c in rewritten_columns:
                if not c.fill.is_empty:
                    data[c.name] = draw(
                        series(
                            index=local_index_strategy,
                            dtype=c.dtype,
                            elements=c.elements,
                            fill=c.fill,
                            unique=c.unique,
                        )
                    )

            return pandas.DataFrame(data, index=index)

        return just_draw_columns()
    else:

        @st.composite
        def assign_rows(draw):
            index = draw(index_strategy)

            result = pandas.DataFrame(
                OrderedDict(
                    (
                        c.name,
                        pandas.Series(
                            np.zeros(dtype=c.dtype, shape=len(index)), dtype=c.dtype
                        ),
                    )
                    for c in rewritten_columns
                ),
                index=index,
            )

            fills = {}

            any_unique = any(c.unique for c in rewritten_columns)

            if any_unique:
                all_seen = [set() if c.unique else None for c in rewritten_columns]
                while all_seen[-1] is None:
                    all_seen.pop()

            for row_index in hrange(len(index)):
                for _ in hrange(5):
                    original_row = draw(rows)
                    row = original_row
                    if isinstance(row, dict):
                        as_list = [None] * len(rewritten_columns)
                        for i, c in enumerate(rewritten_columns):
                            try:
                                as_list[i] = row[c.name]
                            except KeyError:
                                try:
                                    as_list[i] = fills[i]
                                except KeyError:
                                    fills[i] = draw(c.fill)
                                    as_list[i] = fills[i]
                        for k in row:
                            if k not in column_names:
                                raise InvalidArgument(
                                    "Row %r contains column %r not in columns %r)"
                                    % (row, k, [c.name for c in rewritten_columns])
                                )
                        row = as_list
                    if any_unique:
                        has_duplicate = False
                        for seen, value in zip(all_seen, row):
                            if seen is None:
                                continue
                            if value in seen:
                                has_duplicate = True
                                break
                            seen.add(value)
                        if has_duplicate:
                            continue
                    row = list(try_convert(tuple, row, "draw(rows)"))

                    if len(row) > len(rewritten_columns):
                        raise InvalidArgument(
                            (
                                "Row %r contains too many entries. Has %d but "
                                "expected at most %d"
                            )
                            % (original_row, len(row), len(rewritten_columns))
                        )
                    while len(row) < len(rewritten_columns):
                        row.append(draw(rewritten_columns[len(row)].fill))
                    result.iloc[row_index] = row
                    break
                else:
                    reject()
            return result

        return assign_rows()