forked from python-attrs/attrs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move test helpers into an utils module
__init__.py should never contain any code.
- Loading branch information
Showing
7 changed files
with
137 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
[run] | ||
branch = True | ||
source = attr | ||
source = | ||
attr | ||
|
||
[paths] | ||
source = | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |