From f6ffab7be059c251cede240e237c0adee07220b5 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Wed, 21 Oct 2020 16:04:12 +1100 Subject: [PATCH] Refactor Hypothesis strategy for self-tests (#706) * Ease Hypothesis shrinking This is a tiny performance improvement for shrinking. Unconditionally drawing and conditionally *using* these boolean flags minimises the impact of mutating other parts of an input on the structure here, which in turn means more attempts will be valid. * Refactor Hypothesis strategy Using the composite decorator allows for a much clearer imperative description of how the class is constructed. --- tests/strategies.py | 76 ++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 57 deletions(-) diff --git a/tests/strategies.py b/tests/strategies.py index fc4ad50b..a9bc2640 100644 --- a/tests/strategies.py +++ b/tests/strategies.py @@ -46,14 +46,8 @@ def maybe_underscore_prefix(source): to_underscore = not to_underscore -def _create_hyp_class(attrs): - """ - A helper function for Hypothesis to generate attrs classes. - """ - return make_class("HypClass", dict(zip(gen_attr_names(), attrs))) - - -def _create_hyp_nested_strategy(simple_class_strategy): +@st.composite +def _create_hyp_nested_strategy(draw, simple_class_strategy): """ Create a recursive attrs class. @@ -62,47 +56,17 @@ def _create_hyp_nested_strategy(simple_class_strategy): the simpler class, a list of simpler classes, a tuple of simpler classes, an ordered dict or a dict mapping the string "cls" to a simpler class. """ - # Use a tuple strategy to combine simple attributes and an attr class. - def just_class(tup): - combined_attrs = list(tup[0]) - combined_attrs.append(attr.ib(default=attr.Factory(tup[1]))) - return _create_hyp_class(combined_attrs) - - def list_of_class(tup): - default = attr.Factory(lambda: [tup[1]()]) - combined_attrs = list(tup[0]) - combined_attrs.append(attr.ib(default=default)) - return _create_hyp_class(combined_attrs) - - def tuple_of_class(tup): - default = attr.Factory(lambda: (tup[1](),)) - combined_attrs = list(tup[0]) - combined_attrs.append(attr.ib(default=default)) - return _create_hyp_class(combined_attrs) - - def dict_of_class(tup): - default = attr.Factory(lambda: {"cls": tup[1]()}) - combined_attrs = list(tup[0]) - combined_attrs.append(attr.ib(default=default)) - return _create_hyp_class(combined_attrs) - - def ordereddict_of_class(tup): - default = attr.Factory(lambda: OrderedDict([("cls", tup[1]())])) - combined_attrs = list(tup[0]) - combined_attrs.append(attr.ib(default=default)) - return _create_hyp_class(combined_attrs) - - # A strategy producing tuples of the form ([list of attributes], ). - attrs_and_classes = st.tuples(list_of_attrs, simple_class_strategy) - - return st.one_of( - attrs_and_classes.map(just_class), - attrs_and_classes.map(list_of_class), - attrs_and_classes.map(tuple_of_class), - attrs_and_classes.map(dict_of_class), - attrs_and_classes.map(ordereddict_of_class), - ) + cls = draw(simple_class_strategy) + factories = [ + cls, + lambda: [cls()], + lambda: (cls(),), + lambda: {"cls": cls()}, + lambda: OrderedDict([("cls", cls())]), + ] + factory = draw(st.sampled_from(factories)) + attrs = draw(list_of_attrs) + [attr.ib(default=attr.Factory(factory))] + return make_class("HypClass", dict(zip(gen_attr_names(), attrs))) bare_attrs = st.builds(attr.ib, default=st.none()) @@ -177,11 +141,9 @@ def simple_classes( private, and if `private_attrs=False`, no attributes will be private. """ attrs = draw(list_of_attrs) - frozen_flag = draw(st.booleans()) if frozen is None else frozen - slots_flag = draw(st.booleans()) if slots is None else slots - weakref_slot_flag = ( - draw(st.booleans()) if weakref_slot is None else weakref_slot - ) + frozen_flag = draw(st.booleans()) + slots_flag = draw(st.booleans()) + weakref_flag = draw(st.booleans()) if private_attrs is None: attr_names = maybe_underscore_prefix(gen_attr_names()) @@ -202,9 +164,9 @@ def simple_classes( return make_class( "HypClass", cls_dict, - slots=slots_flag, - frozen=frozen_flag, - weakref_slot=weakref_slot_flag, + slots=slots_flag if slots is None else slots, + frozen=frozen_flag if frozen is None else frozen, + weakref_slot=weakref_flag if weakref_slot is None else weakref_slot, )