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

decimals() fails on a range entirely between two integers #739

Closed
teepark opened this issue Jul 26, 2017 · 2 comments · Fixed by #791
Closed

decimals() fails on a range entirely between two integers #739

teepark opened this issue Jul 26, 2017 · 2 comments · Fixed by #791
Assignees
Labels
bug something is clearly wrong here

Comments

@teepark
Copy link

teepark commented Jul 26, 2017

It appears decimals() is passing its min_value and max_value down to a call to integers(), which fails when there couldn't possibly be an integer between them (but there could be a decimal).

minimal reproducible example:

from decimal import Decimal
from hypothesis import given, strategies as st

@given(st.decimals(min_value=Decimal('0.1'), max_value=Decimal('0.3')))
def test_tight_decimal_range(num):
    pass
tmpproj.test_tight_decimal_range ... FAIL

======================================================================
FAIL: tmpproj.test_tight_decimal_range
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/private/var/folders/fl/lt_vq2n17117s6n2qp9s7z2m0000gn/T/tmp.WjAPvqmP/tmpproj.py", line 5, in test_tight_decimal_range
    def test_tight_decimal_range(num):
  File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 634, in wrapped_test
    state.run()
  File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 478, in run
    runner.run()
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 235, in run
    self._run()
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 387, in _run
    self.test_function(data)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/engine.py", line 97, in test_function
    self._test_function(data)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 465, in evaluate_test_data
    escalate_hypothesis_internal_error()
  File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 448, in evaluate_test_data
    self.search_strategy, self.test,
  File "/usr/local/lib/python2.7/site-packages/hypothesis/executors.py", line 58, in default_new_style_executor
    return function(data)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/core.py", line 103, in run
    args, kwargs = data.draw(search_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 117, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 63, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 59, in newtuple
    return tuple(xs)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 63, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 279, in do_draw
    return self.pack(self.mapped_strategy.do_draw(data))
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 117, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 279, in do_draw
    return self.pack(self.mapped_strategy.do_draw(data))
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 63, in do_draw
    data.draw(e) for e in self.element_strategies
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 59, in newtuple
    return tuple(xs)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/collections.py", line 63, in <genexpr>
    data.draw(e) for e in self.element_strategies
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 117, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 225, in do_draw
    return data.draw(self.element_strategies[i])
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/strategies.py", line 279, in do_draw
    return self.pack(self.mapped_strategy.do_draw(data))
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 117, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/flatmapped.py", line 44, in do_draw
    return data.draw(self.expand(source))
  File "/usr/local/lib/python2.7/site-packages/hypothesis/internal/conjecture/data.py", line 104, in draw
    return strategy.do_draw(self)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 117, in do_draw
    return data.draw(self.wrapped_strategy)
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 79, in wrapped_strategy
    *[unwrap_strategies(s) for s in self.__args],
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 39, in unwrap_strategies
    s = s.wrapped_strategy
  File "/usr/local/lib/python2.7/site-packages/hypothesis/searchstrategy/deferred.py", line 81, in wrapped_strategy
    for k, v in self.__kwargs.items()})
  File "/usr/local/lib/python2.7/site-packages/hypothesis/strategies.py", line 232, in integers
    assert min_int_value <= max_int_value
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.014s

FAILED (failures=1)
@teepark
Copy link
Author

teepark commented Jul 26, 2017

for now the trick of widening the range and then mapping it down tighter is a viable workaround:

st.decimals(min_value=10, max_value=30).map(lambda d: d / 100)

@Zac-HD Zac-HD added the bug something is clearly wrong here label Jul 26, 2017
@Zac-HD
Copy link
Member

Zac-HD commented Jul 26, 2017

Tracing so far: this can happen when decimals() uses fractions() to provide the data. fractions() then passes through the bounding values (which are Decimal objects) to integers(), which multiplies by the denominator then casts them to int and thus collapses ranges of integer i .. i + 1/denom (denom being any natural number).

I think this is actually best fixed in fractions(), which should accept Fraction objects as bounds, convert to safe integers, and divide the output (carefully handling the max_denominator, too...). The call in decimals() would then simply cast its arguments to fractions. integers() should use floor for it's upper bound and ceil for the lower bound.

Note that if fractions() is currently given non-integer bounds it may produce out-of-bounds examples and/or fail to produce in-bounds examples due to the casting mentioned above.

Meta-comment: we should write tests to ensure that when bounds are given to a strategy, the examples all lie within the given bounds. I'd also like to test for completeness, but that's probably too expensive to do reliably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something is clearly wrong here
Projects
None yet
2 participants