Feature/better simple classes (#119)
This commit is contained in:
parent
6771640853
commit
0c8211d922
|
@ -132,10 +132,13 @@ class TestAsDict(object):
|
||||||
} == res
|
} == res
|
||||||
assert isinstance(res, dict_factory)
|
assert isinstance(res, dict_factory)
|
||||||
|
|
||||||
@given(simple_classes(), st.sampled_from(MAPPING_TYPES))
|
@given(simple_classes(private_attrs=False), st.sampled_from(MAPPING_TYPES))
|
||||||
def test_roundtrip(self, cls, dict_class):
|
def test_roundtrip(self, cls, dict_class):
|
||||||
"""
|
"""
|
||||||
Test dumping to dicts and back for Hypothesis-generated classes.
|
Test dumping to dicts and back for Hypothesis-generated classes.
|
||||||
|
|
||||||
|
Private attributes don't round-trip (the attribute name is different
|
||||||
|
than the initializer argument).
|
||||||
"""
|
"""
|
||||||
instance = cls()
|
instance = cls()
|
||||||
dict_instance = asdict(instance, dict_factory=dict_class)
|
dict_instance = asdict(instance, dict_factory=dict_class)
|
||||||
|
@ -359,22 +362,27 @@ class TestAssoc(object):
|
||||||
assert i1 is not i2
|
assert i1 is not i2
|
||||||
assert i1 == i2
|
assert i1 == i2
|
||||||
|
|
||||||
@given(simple_classes(), st.integers())
|
@given(simple_classes(), st.data())
|
||||||
def test_change(self, C, val):
|
def test_change(self, C, data):
|
||||||
"""
|
"""
|
||||||
Changes work.
|
Changes work.
|
||||||
"""
|
"""
|
||||||
# Take the first attribute, and change it.
|
# Take the first attribute, and change it.
|
||||||
assume(fields(C)) # Skip classes with no attributes.
|
assume(fields(C)) # Skip classes with no attributes.
|
||||||
|
field_names = [a.name for a in fields(C)]
|
||||||
original = C()
|
original = C()
|
||||||
attribute = fields(C)[0]
|
chosen_names = data.draw(st.sets(st.sampled_from(field_names)))
|
||||||
changed = assoc(original, **{attribute.name: val})
|
change_dict = {name: data.draw(st.integers())
|
||||||
assert getattr(changed, attribute.name) == val
|
for name in chosen_names}
|
||||||
|
changed = assoc(original, **change_dict)
|
||||||
|
for k, v in change_dict.items():
|
||||||
|
assert getattr(changed, k) == v
|
||||||
|
|
||||||
@given(simple_classes())
|
@given(simple_classes())
|
||||||
def test_unknown(self, C):
|
def test_unknown(self, C):
|
||||||
"""
|
"""
|
||||||
Wanting to change an unknown attribute raises a ValueError.
|
Wanting to change an unknown attribute raises an
|
||||||
|
AttrsAttributeNotFoundError.
|
||||||
"""
|
"""
|
||||||
# No generated class will have a four letter attribute.
|
# No generated class will have a four letter attribute.
|
||||||
with pytest.raises(AttrsAttributeNotFoundError) as e:
|
with pytest.raises(AttrsAttributeNotFoundError) as e:
|
||||||
|
|
|
@ -74,6 +74,16 @@ def gen_attr_names():
|
||||||
yield outer + inner
|
yield outer + inner
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_underscore_prefix(source):
|
||||||
|
"""
|
||||||
|
A generator to sometimes prepend an underscore.
|
||||||
|
"""
|
||||||
|
to_underscore = False
|
||||||
|
for val in source:
|
||||||
|
yield val if not to_underscore else '_' + val
|
||||||
|
to_underscore = not to_underscore
|
||||||
|
|
||||||
|
|
||||||
def _create_hyp_class(attrs):
|
def _create_hyp_class(attrs):
|
||||||
"""
|
"""
|
||||||
A helper function for Hypothesis to generate attrs classes.
|
A helper function for Hypothesis to generate attrs classes.
|
||||||
|
@ -164,7 +174,7 @@ list_of_attrs = st.lists(simple_attrs, average_size=3, max_size=9)
|
||||||
|
|
||||||
|
|
||||||
@st.composite
|
@st.composite
|
||||||
def simple_classes(draw, slots=None, frozen=None):
|
def simple_classes(draw, slots=None, frozen=None, private_attrs=None):
|
||||||
"""
|
"""
|
||||||
A strategy that generates classes with default non-attr attributes.
|
A strategy that generates classes with default non-attr attributes.
|
||||||
|
|
||||||
|
@ -173,21 +183,32 @@ def simple_classes(draw, slots=None, frozen=None):
|
||||||
@attr.s(slots=True, frozen=True)
|
@attr.s(slots=True, frozen=True)
|
||||||
class HypClass:
|
class HypClass:
|
||||||
a = attr.ib(default=1)
|
a = attr.ib(default=1)
|
||||||
b = attr.ib(default=None)
|
_b = attr.ib(default=None)
|
||||||
c = attr.ib(default='text')
|
c = attr.ib(default='text')
|
||||||
d = attr.ib(default=1.0)
|
_d = attr.ib(default=1.0)
|
||||||
c = attr.ib(default={'t': 1})
|
c = attr.ib(default={'t': 1})
|
||||||
|
|
||||||
By default, all combinations of slots and frozen classes will be generated.
|
By default, all combinations of slots and frozen classes will be generated.
|
||||||
If `slots=True` is passed in, only slots classes will be generated, and
|
If `slots=True` is passed in, only slots classes will be generated, and
|
||||||
if `slots=False` is passed in, no slot classes will be generated. The same
|
if `slots=False` is passed in, no slot classes will be generated. The same
|
||||||
applies to `frozen`.
|
applies to `frozen`.
|
||||||
|
|
||||||
|
By default, some attributes will be private (i.e. prefixed with an
|
||||||
|
underscore). If `private_attrs=True` is passed in, all attributes will be
|
||||||
|
private, and if `private_attrs=False`, no attributes will be private.
|
||||||
"""
|
"""
|
||||||
attrs = draw(list_of_attrs)
|
attrs = draw(list_of_attrs)
|
||||||
frozen_flag = draw(st.booleans()) if frozen is None else frozen
|
frozen_flag = draw(st.booleans()) if frozen is None else frozen
|
||||||
slots_flag = draw(st.booleans()) if slots is None else slots
|
slots_flag = draw(st.booleans()) if slots is None else slots
|
||||||
|
|
||||||
cls_dict = dict(zip(gen_attr_names(), attrs))
|
if private_attrs is None:
|
||||||
|
attr_names = maybe_underscore_prefix(gen_attr_names())
|
||||||
|
elif private_attrs is True:
|
||||||
|
attr_names = ('_' + n for n in gen_attr_names())
|
||||||
|
elif private_attrs is False:
|
||||||
|
attr_names = gen_attr_names()
|
||||||
|
|
||||||
|
cls_dict = dict(zip(attr_names, attrs))
|
||||||
post_init_flag = draw(st.booleans())
|
post_init_flag = draw(st.booleans())
|
||||||
if post_init_flag:
|
if post_init_flag:
|
||||||
def post_init(self):
|
def post_init(self):
|
||||||
|
|
Loading…
Reference in New Issue