diff --git a/.coveragerc b/.coveragerc index d17a6aa4..093c1194 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,6 +1,7 @@ [run] branch = True -source = attr +source = + attr [paths] source = diff --git a/tests/__init__.py b/tests/__init__.py index 40766556..e69de29b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,123 +0,0 @@ -from __future__ import absolute_import, division, print_function - -import keyword -import string - -from hypothesis import strategies as st - -import attr -from attr import Attribute -from attr._make import NOTHING, make_class - - -def simple_class(cmp=False, repr=False, hash=False, slots=False): - """ - Return a new simple class. - """ - return make_class( - "C", ["a", "b"], - cmp=cmp, repr=repr, hash=hash, init=True, slots=slots - ) - - -def simple_attr(name, default=NOTHING, validator=None, repr=True, - cmp=True, hash=True, init=True): - """ - Return an attribute with a name and no other bells and whistles. - """ - return Attribute( - name=name, default=default, validator=validator, repr=repr, - cmp=cmp, hash=hash, init=init - ) - - -class TestSimpleClass(object): - """ - Tests for the testing helper function `make_class`. - """ - def test_returns_class(self): - """ - Returns a class object. - """ - assert type is simple_class().__class__ - - def returns_distinct_classes(self): - """ - Each call returns a completely new class. - """ - assert simple_class() is not simple_class() - - -def _gen_attr_names(): - """ - Generate names for attributes, 'a'...'z', then 'aa'...'zz'. - - ~702 different attribute names should be enough in practice. - - Some short strings (such as 'as') are keywords, so we skip them. - """ - lc = string.ascii_lowercase - for c in lc: - yield c - for outer in lc: - for inner in lc: - res = outer + inner - if keyword.iskeyword(res): - continue - yield outer + inner - - -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): - """ - Create a recursive attrs class. - - Given a strategy for building (simpler) classes, create and return - a strategy for building classes that have the simpler class as an - attribute. - """ - # 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 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) - - return st.one_of(st.tuples(st.lists(simple_attrs), simple_class_strategy) - .map(just_class), - st.tuples(st.lists(simple_attrs), simple_class_strategy) - .map(list_of_class)) - -bare_attrs = st.just(attr.ib(default=None)) -int_attrs = st.integers().map(lambda i: attr.ib(default=i)) -str_attrs = st.text().map(lambda s: attr.ib(default=s)) -float_attrs = st.floats().map(lambda f: attr.ib(default=f)) -dict_attrs = (st.dictionaries(keys=st.text(), values=st.integers()) - .map(lambda d: attr.ib(default=d))) - -simple_attrs = st.one_of(bare_attrs, int_attrs, str_attrs, float_attrs, - dict_attrs) - -simple_classes = st.lists(simple_attrs).map(_create_hyp_class) - -# Ok, so st.recursive works by taking a base strategy (in this case, -# simple_classes) and a special function. This function receives a strategy, -# and returns another strategy (building on top of the base strategy). -nested_classes = st.recursive(simple_classes, _create_hyp_nested_strategy) diff --git a/tests/test_dunders.py b/tests/test_dunders.py index 2bfb2641..3a734cff 100644 --- a/tests/test_dunders.py +++ b/tests/test_dunders.py @@ -10,7 +10,7 @@ import pytest from hypothesis import given from hypothesis.strategies import booleans -from . import simple_attr, simple_class +from .utils import simple_attr, simple_class from attr._make import ( Factory, NOTHING, diff --git a/tests/test_funcs.py b/tests/test_funcs.py index d7b3193e..c8d45d88 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -10,7 +10,7 @@ import pytest from hypothesis import given, strategies as st -from . import simple_classes, nested_classes +from .utils import simple_classes, nested_classes from attr._funcs import ( asdict, diff --git a/tests/test_make.py b/tests/test_make.py index 97c5cb99..ba25f586 100644 --- a/tests/test_make.py +++ b/tests/test_make.py @@ -5,10 +5,10 @@ Tests for `attr._make`. from __future__ import absolute_import, division, print_function import pytest + from hypothesis import given from hypothesis.strategies import booleans, sampled_from -from . import simple_attr, simple_attrs from attr import _config from attr._compat import PY3 from attr._make import ( @@ -23,6 +23,8 @@ from attr._make import ( validate, ) +from .utils import simple_attr, simple_attrs + attrs = simple_attrs.map(lambda c: Attribute.from_counting_attr('name', c)) diff --git a/tests/test_validators.py b/tests/test_validators.py index 7be59b60..5e08fe9e 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -9,7 +9,8 @@ import zope.interface from attr.validators import instance_of, provides, optional from attr._compat import TYPE -from . import simple_attr + +from .utils import simple_attr class TestInstanceOf(object): diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..ca85ea56 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,128 @@ +""" +Common helper functions for tests. +""" + +from __future__ import absolute_import, division, print_function + +import keyword +import string + +from hypothesis import strategies as st + +import attr + +from attr import Attribute +from attr._make import NOTHING, make_class + + +def simple_class(cmp=False, repr=False, hash=False, slots=False): + """ + Return a new simple class. + """ + return make_class( + "C", ["a", "b"], + cmp=cmp, repr=repr, hash=hash, init=True, slots=slots + ) + + +def simple_attr(name, default=NOTHING, validator=None, repr=True, + cmp=True, hash=True, init=True): + """ + Return an attribute with a name and no other bells and whistles. + """ + return Attribute( + name=name, default=default, validator=validator, repr=repr, + cmp=cmp, hash=hash, init=init + ) + + +class TestSimpleClass(object): + """ + Tests for the testing helper function `make_class`. + """ + def test_returns_class(self): + """ + Returns a class object. + """ + assert type is simple_class().__class__ + + def returns_distinct_classes(self): + """ + Each call returns a completely new class. + """ + assert simple_class() is not simple_class() + + +def _gen_attr_names(): + """ + Generate names for attributes, 'a'...'z', then 'aa'...'zz'. + + ~702 different attribute names should be enough in practice. + + Some short strings (such as 'as') are keywords, so we skip them. + """ + lc = string.ascii_lowercase + for c in lc: + yield c + for outer in lc: + for inner in lc: + res = outer + inner + if keyword.iskeyword(res): + continue + yield outer + inner + + +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): + """ + Create a recursive attrs class. + + Given a strategy for building (simpler) classes, create and return + a strategy for building classes that have the simpler class as an + attribute. + """ + # 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 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) + + return st.one_of(st.tuples(st.lists(simple_attrs), simple_class_strategy) + .map(just_class), + st.tuples(st.lists(simple_attrs), simple_class_strategy) + .map(list_of_class)) + +bare_attrs = st.just(attr.ib(default=None)) +int_attrs = st.integers().map(lambda i: attr.ib(default=i)) +str_attrs = st.text().map(lambda s: attr.ib(default=s)) +float_attrs = st.floats().map(lambda f: attr.ib(default=f)) +dict_attrs = (st.dictionaries(keys=st.text(), values=st.integers()) + .map(lambda d: attr.ib(default=d))) + +simple_attrs = st.one_of(bare_attrs, int_attrs, str_attrs, float_attrs, + dict_attrs) + +simple_classes = st.lists(simple_attrs).map(_create_hyp_class) + +# Ok, so st.recursive works by taking a base strategy (in this case, +# simple_classes) and a special function. This function receives a strategy, +# and returns another strategy (building on top of the base strategy). +nested_classes = st.recursive(simple_classes, _create_hyp_nested_strategy)