-
Notifications
You must be signed in to change notification settings - Fork 586
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
Proposal for a more flexible bounded floats strategy #1622
Comments
|
Happily, we have implemented If you also need further floats strategies or arguments, I'd suggest starting with an experimental package to shake out the API - once you have some more experience with it we'll be able to judge whether the extra power justifies the risk of confusing new users (and the pain of migrating compatibility layers). TLDR, I'm sympathetic but in this case conservative! |
|
How would we feel about |
We already have a lot of logic to deal with tricky endpoints, so it would not just be a matter of incrementing or decrementing the float bound. Take the example of On the other hand, In short, I think the |
|
Using Hypothesis gives us some powerful tools to adapt strategies in So in practice, though I could use |
If there's a pretty trivial way to exclude one or both of the endpoints, what's the difficulty with us implementing the feature in that pretty trivial way? |
|
Also if the main objection is just that it seems like |
|
I have a preference for |
My problem isn't implementation - it would be 4-10 LoC, and we already have piles of nasty internal logic for floats - it's whether users can easily form a correct mental model of how
Basically I just don't think this option is a good fit for Hypothesis core, because if you need it you can just define a helper in your own project: def floats(min_value=None, max_value=None, *, exclude_min=False, exclude_min=True, **kw):
s = st.floats(min_value, max_value, **kw)
if exclude_min is not None:
s = s.filter(lambda x: x != min_value)
if exclude_max is not None:
s = s.filter(lambda x: x != max_value)
return sOur contributing guide mentions a preference for composable primitives - IMO the impact on other (especially new) users is too large to justify the convenience of a feature that is easy enough to compose downstream. |
I think this is a bad argument. A lot of functionality in Hypothesis core is stuff you could easily add helpers for to your own project, and that's a good thing. The aggregate of many trivial usability features is not a trivial usability improvement, but a large one.
TBH adding them to the other strategies which accept
I'm kinda with Rob, I think we should deprecate the possibility of passing
I don't think you've convincingly argued a large impact on users at all TBH. The proposed feature has straightforward semantics, is a fairly common use case, and the absolute worst case scenario is that in a scenario that is already highly misleading the user might be slightly more mislead if they don't read the documentation. |
|
I've said my piece, and I'm happy to go with the consensus for adding them 😄
Agreed! I've opened #1625 as a good-first-issue for someone to do this in Hacktoberfest. |
Motivation
In many of the applications where I benefit from using Hypothesis, I find myself needing a strategy for generating floats drawn from half-open intervals. The existing
hypothesis.strategies.floatsstrategy draws from a closed interval, and though it's straightforward enough to exclude one or both of the limits usingassume()in the body of the test, this makes the test somewhat harder to quickly comprehend, and theassume()is easily forgotten.Proposal
This is a proposal for a strategy capable for generating floats drawn from half-open or open internals, or modification of the existing
floatsstrategy to support the convenient specification of such intervals.Interface
I've been using a strategy called
bounded_floatswhich instead ofmin_valueandmax_valueto specify the interval, usesinclusive_min_valuexorexclusive_min_value, andinclusive_max_valuexorexclusive_max_valuearguments. I accept these are somewhat wordy, but they clearly express the intent. In addition, theallow_nan,allow_infinityandwidtharguments are also supported.Typical use would be (from real code):
Implementation
I've been using the following simple wrapper for the existing
floatsstrategy:Alternatives
I've thought about, and experimented with, some alternative designs, which are worth considering:
Separate functions for
open_floats(),right_half_open_floats(),left_half_open_floats(),closed_floats(), whereclosed_floats()is just an alias for the existingfloats(). I found it difficult to remember whether the left or right bound was included or excluded.I tried
open_min_valueandclosed_min_value, etc for the argument names, but it turns out that many programmers aren't familiar with the open/closed interval terminology. In any case,inclusiveandexclusivehave the pleasing aesthetic property of having the same number of characters, as well as being widely understood.I haven't tried this, but one idea would be extend the existing
floatsstrategy to support at leastexclusive_min_valueandexclusive_max_valuein addition to the existingmin_valueandmax_valuearguments, which would be retained for backwards compatibility. It's also possible to supportinclusive_min_valueandinclusive_max_valuealiases for the existing arguments in the spirit of allowing clearer interval specifications.All this becomes unnecessary if you can easily generate the floating point numbers immediately inside the discrete closed interval which corresponds to the discrete (half-)open interval. In C and C++ we have the
nextafterandnexttowardfunctions which return the adjacent representable floating point numbers. Unfortunately, these aren't exposed in Python'smathmodule. Using functions like them, it should be possible to using the existingfloatsstrategy like this:though this could get messy when trying to account for the
widthargument tofloats.If there's sufficient interest in this proposal, I'll submit a pull request.
The text was updated successfully, but these errors were encountered: