Closed ysangkok closed 3 years ago
As the docs note, sampled_from(Enum)
works directly, and even has special handling for Flag
enums to generate combinations of options.
I actually don't think we should change the semantics of builds()
away from "calls the target with arguments", even when there's a clear alternative interpretation (though it's not that clear given that enums are actually callable!). It's not a bad API style for interactive tools, but as a testing framework we prioritize clear and simple mental models to minimize the risk of surprising test behaviour.
However, I'd be happy to detect this particular problem and raise an error with a helpful message explaining what to do instead.
Ok! The confusion mainly stems from the fact that I can build dataclasses using builds
, even if they contain Enums. It seemed odd to me that I would call a different function based on whether the outermost type is a sum type or a product type. I would very much appreciate a more helpful error message! Thanks.
It seemed odd to me that I would call a different function based on whether the outermost type is a sum type or a product type.
Ah - the distinction is actually "builds(target)
calls the target, sampled_from(target)
returns elements from the target sequence". Sum-type vs product-type is a nice mental model to have, but not quite applicable here.
Knowing that's the distinction you had in mind will help me write the new error message though - thanks!
Thanks a lot @Zac-HD , I appreciate it a lot!
But how do we generate enums, though? I've tried
@st.composite
def generate_enum(draw):
return draw(
st.builds(
Enum,
st.from_regex(r"(?a)[_a-zA-Z][_a-zA-Z0-9]*"),
st.from_regex(r"(?a)[_a-zA-Z][_a-zA-Z0-9]*( [_a-zA-Z][_a-zA-Z0-9]*)*"),
)
)
But got
TypeError: Attempted to reuse key: 'A'
and
ValueError: type name must not contain null characters
I also saw #2923 . Maybe I'm just dumb. Because what I'm trying is not working
This issue is about generating instances of a particular Enum subclass. To generate arbitrary Enum types, you could
from enum import Enum
from hypothesis import strategies as st
def enums():
names = st.text().filter(str.isidentifier) # or st.from_regex(), etc.
values = st.lists(names, min_size=1, unique=True).map(" ".join)
return st.builds(Enum, names, values)
though a more efficient strategy for Python identifiers can be found here. You could also be more general about the values with st.lists(st.tuples(names, st.from_type(Hashable)), min_size=1, unique_by=itemgetter(0))
but it's unclear whether that's worth the trouble.
Funny, because I also tried a little harder with making the enum strategy and I got (where variable_names()
would be the identifier-generating strategy)
@st.composite
def generate_enum(draw):
return draw(
st.builds(
Enum,
variable_names(),
st.dictionaries(variable_names(), st.text()),
),
)
I think you guys should really add two new strategies: Enums and/or identifiers
I think you guys should really add two new strategies: Enums and/or identifiers
What is insufficient about the strategy that you and @Zac-HD both posted? It makes standard use of composite
and builds
, and it is concise and easy to reason about.
I think you guys should really add two new strategies: Enums and/or identifiers
What is insufficient about the strategy that you and @Zac-HD both posted? It makes standard use of
composite
andbuilds
, and it is concise and easy to reason about.
Hmm, true. Though it did take me a while to figure. I guess we shouldn't add too much extra strategies
As the docs note,
sampled_from(Enum)
works directly, and even has special handling forFlag
enums to generate combinations of options.
I get
hypothesis.errors.InvalidArgument: Cannot sample from generate_enum(), not an ordered collection.
Because generate_enum()
returns a strategy, not an ordered collection.
If one tries
builds(Enum)
, it fails horribly:It would be nice if the right way was documented, or if
builds
detected that it was being called on an enum. It is a standard library class.sampled_from(list(Enum))
does work, but it may not be obvious to everybody.This was tested with version 5.41.5 on Python 3.9.0rc2.