diff --git a/src/attr/_make.py b/src/attr/_make.py index f6eddefddd6aa07..e5310afbe8bfa92 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -7,6 +7,9 @@ from ._compat import exec_, iteritems, isclass, iterkeys from .exceptions import FrozenInstanceError +# This is used at least twice, so cache it here. +_obj_setattr = object.__setattr__ + class _Nothing(object): """ @@ -414,7 +417,7 @@ def _add_init(cls, frozen): if frozen is True: # Save the lookup overhead in __init__ if we need to circumvent # immutability. - globs["_cached_setattr"] = object.__setattr__ + globs["_cached_setattr"] = _obj_setattr exec_(bytecode, globs, locs) init = locs["__init__"] @@ -596,17 +599,19 @@ class Attribute(object): _optional = {"convert": None} - def __init__(self, **kw): - if len(kw) > len(Attribute.__slots__): - raise TypeError("Too many arguments.") - for a in Attribute.__slots__: - try: - object.__setattr__(self, a, kw[a]) - except KeyError: - if a in Attribute._optional: - object.__setattr__(self, a, self._optional[a]) - else: - raise TypeError("Missing argument '{arg}'.".format(arg=a)) + def __init__(self, name, default, validator, repr, cmp, hash, init, + convert=None): + # Cache this descriptor here to speed things up later. + __bound_setattr = _obj_setattr.__get__(self, Attribute) + + __bound_setattr('name', name) + __bound_setattr('default', default) + __bound_setattr('validator', validator) + __bound_setattr('repr', repr) + __bound_setattr('cmp', cmp) + __bound_setattr('hash', hash) + __bound_setattr('init', init) + __bound_setattr('convert', convert) def __setattr__(self, name, value): raise FrozenInstanceError() diff --git a/tests/test_funcs.py b/tests/test_funcs.py index 1216e431f4aa25d..404bcc9c2b6ed92 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -79,7 +79,8 @@ def assert_proper_dict_class(obj, obj_dict): assert isinstance(obj_dict[field.name], dict_class) for key, val in field_val.items(): if has(val.__class__): - assert_proper_dict_class(val, obj_dict[key]) + assert_proper_dict_class(val, + obj_dict[field.name][key]) assert_proper_dict_class(obj, obj_dict) diff --git a/tests/test_make.py b/tests/test_make.py index bf1838e4661e95d..34e8af87c9087fc 100644 --- a/tests/test_make.py +++ b/tests/test_make.py @@ -13,7 +13,6 @@ from attr._compat import PY2 from attr._make import ( Attribute, - NOTHING, _CountingAttr, _transform_attrs, attr, @@ -294,29 +293,6 @@ class D(object): pass -class TestAttribute(object): - """ - Tests for `Attribute`. - """ - def test_missing_argument(self): - """ - Raises `TypeError` if an Argument is missing. - """ - with pytest.raises(TypeError) as e: - Attribute(default=NOTHING, validator=None) - assert ("Missing argument 'name'.",) == e.value.args - - def test_too_many_arguments(self): - """ - Raises `TypeError` if extra arguments are passed. - """ - with pytest.raises(TypeError) as e: - Attribute(name="foo", default=NOTHING, - factory=NOTHING, validator=None, - repr=True, cmp=True, hash=True, init=True, convert=None) - assert ("Too many arguments.",) == e.value.args - - class TestMakeClass(object): """ Tests for `make_class`. diff --git a/tests/utils.py b/tests/utils.py index 568db6ab7be3689..e06cd597a8b0923 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -84,8 +84,9 @@ 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. + a strategy for building classes that have as an attribute: either just + the simpler class, a list of simpler classes, 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): @@ -105,10 +106,13 @@ def dict_of_class(tup): 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)) + # 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(dict_of_class)) bare_attrs = st.just(attr.ib(default=None)) int_attrs = st.integers().map(lambda i: attr.ib(default=i)) @@ -121,8 +125,8 @@ def dict_of_class(tup): dict_attrs) # Python functions support up to 255 arguments. -simple_classes = (st.lists(simple_attrs, average_size=9, max_size=50) - .map(_create_hyp_class)) +list_of_attrs = st.lists(simple_attrs, average_size=9, max_size=50) +simple_classes = list_of_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,